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 /* needed if different viewport properties defined for playing */
3725 ChangeViewportPropertiesIfNeeded();
3729 DrawCompleteVideoDisplay();
3733 InitGameControlValues();
3735 /* don't play tapes over network */
3736 network_playing = (options.network && !tape.playing);
3738 for (i = 0; i < MAX_PLAYERS; i++)
3740 struct PlayerInfo *player = &stored_player[i];
3742 player->index_nr = i;
3743 player->index_bit = (1 << i);
3744 player->element_nr = EL_PLAYER_1 + i;
3746 player->present = FALSE;
3747 player->active = FALSE;
3748 player->mapped = FALSE;
3750 player->killed = FALSE;
3751 player->reanimated = FALSE;
3754 player->effective_action = 0;
3755 player->programmed_action = 0;
3758 player->score_final = 0;
3760 player->gems_still_needed = level.gems_needed;
3761 player->sokobanfields_still_needed = 0;
3762 player->lights_still_needed = 0;
3763 player->friends_still_needed = 0;
3765 for (j = 0; j < MAX_NUM_KEYS; j++)
3766 player->key[j] = FALSE;
3768 player->num_white_keys = 0;
3770 player->dynabomb_count = 0;
3771 player->dynabomb_size = 1;
3772 player->dynabombs_left = 0;
3773 player->dynabomb_xl = FALSE;
3775 player->MovDir = initial_move_dir;
3778 player->GfxDir = initial_move_dir;
3779 player->GfxAction = ACTION_DEFAULT;
3781 player->StepFrame = 0;
3783 player->initial_element = player->element_nr;
3784 player->artwork_element =
3785 (level.use_artwork_element[i] ? level.artwork_element[i] :
3786 player->element_nr);
3787 player->use_murphy = FALSE;
3789 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3790 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3792 player->gravity = level.initial_player_gravity[i];
3794 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3796 player->actual_frame_counter = 0;
3798 player->step_counter = 0;
3800 player->last_move_dir = initial_move_dir;
3802 player->is_active = FALSE;
3804 player->is_waiting = FALSE;
3805 player->is_moving = FALSE;
3806 player->is_auto_moving = FALSE;
3807 player->is_digging = FALSE;
3808 player->is_snapping = FALSE;
3809 player->is_collecting = FALSE;
3810 player->is_pushing = FALSE;
3811 player->is_switching = FALSE;
3812 player->is_dropping = FALSE;
3813 player->is_dropping_pressed = FALSE;
3815 player->is_bored = FALSE;
3816 player->is_sleeping = FALSE;
3818 player->frame_counter_bored = -1;
3819 player->frame_counter_sleeping = -1;
3821 player->anim_delay_counter = 0;
3822 player->post_delay_counter = 0;
3824 player->dir_waiting = initial_move_dir;
3825 player->action_waiting = ACTION_DEFAULT;
3826 player->last_action_waiting = ACTION_DEFAULT;
3827 player->special_action_bored = ACTION_DEFAULT;
3828 player->special_action_sleeping = ACTION_DEFAULT;
3830 player->switch_x = -1;
3831 player->switch_y = -1;
3833 player->drop_x = -1;
3834 player->drop_y = -1;
3836 player->show_envelope = 0;
3838 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3840 player->push_delay = -1; /* initialized when pushing starts */
3841 player->push_delay_value = game.initial_push_delay_value;
3843 player->drop_delay = 0;
3844 player->drop_pressed_delay = 0;
3846 player->last_jx = -1;
3847 player->last_jy = -1;
3851 player->shield_normal_time_left = 0;
3852 player->shield_deadly_time_left = 0;
3854 player->inventory_infinite_element = EL_UNDEFINED;
3855 player->inventory_size = 0;
3857 if (level.use_initial_inventory[i])
3859 for (j = 0; j < level.initial_inventory_size[i]; j++)
3861 int element = level.initial_inventory_content[i][j];
3862 int collect_count = element_info[element].collect_count_initial;
3865 if (!IS_CUSTOM_ELEMENT(element))
3868 if (collect_count == 0)
3869 player->inventory_infinite_element = element;
3871 for (k = 0; k < collect_count; k++)
3872 if (player->inventory_size < MAX_INVENTORY_SIZE)
3873 player->inventory_element[player->inventory_size++] = element;
3877 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3878 SnapField(player, 0, 0);
3880 player->LevelSolved = FALSE;
3881 player->GameOver = FALSE;
3883 player->LevelSolved_GameWon = FALSE;
3884 player->LevelSolved_GameEnd = FALSE;
3885 player->LevelSolved_PanelOff = FALSE;
3886 player->LevelSolved_SaveTape = FALSE;
3887 player->LevelSolved_SaveScore = FALSE;
3888 player->LevelSolved_CountingTime = 0;
3889 player->LevelSolved_CountingScore = 0;
3891 map_player_action[i] = i;
3894 network_player_action_received = FALSE;
3896 #if defined(NETWORK_AVALIABLE)
3897 /* initial null action */
3898 if (network_playing)
3899 SendToServer_MovePlayer(MV_NONE);
3908 TimeLeft = level.time;
3911 ScreenMovDir = MV_NONE;
3915 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3917 AllPlayersGone = FALSE;
3919 game.yamyam_content_nr = 0;
3920 game.robot_wheel_active = FALSE;
3921 game.magic_wall_active = FALSE;
3922 game.magic_wall_time_left = 0;
3923 game.light_time_left = 0;
3924 game.timegate_time_left = 0;
3925 game.switchgate_pos = 0;
3926 game.wind_direction = level.wind_direction_initial;
3928 #if !USE_PLAYER_GRAVITY
3929 game.gravity = FALSE;
3930 game.explosions_delayed = TRUE;
3933 game.lenses_time_left = 0;
3934 game.magnify_time_left = 0;
3936 game.ball_state = level.ball_state_initial;
3937 game.ball_content_nr = 0;
3939 game.envelope_active = FALSE;
3941 /* set focus to local player for network games, else to all players */
3942 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3943 game.centered_player_nr_next = game.centered_player_nr;
3944 game.set_centered_player = FALSE;
3946 if (network_playing && tape.recording)
3948 /* store client dependent player focus when recording network games */
3949 tape.centered_player_nr_next = game.centered_player_nr_next;
3950 tape.set_centered_player = TRUE;
3953 for (i = 0; i < NUM_BELTS; i++)
3955 game.belt_dir[i] = MV_NONE;
3956 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3959 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3960 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3962 SCAN_PLAYFIELD(x, y)
3964 Feld[x][y] = level.field[x][y];
3965 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3966 ChangeDelay[x][y] = 0;
3967 ChangePage[x][y] = -1;
3968 #if USE_NEW_CUSTOM_VALUE
3969 CustomValue[x][y] = 0; /* initialized in InitField() */
3971 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3973 WasJustMoving[x][y] = 0;
3974 WasJustFalling[x][y] = 0;
3975 CheckCollision[x][y] = 0;
3976 CheckImpact[x][y] = 0;
3978 Pushed[x][y] = FALSE;
3980 ChangeCount[x][y] = 0;
3981 ChangeEvent[x][y] = -1;
3983 ExplodePhase[x][y] = 0;
3984 ExplodeDelay[x][y] = 0;
3985 ExplodeField[x][y] = EX_TYPE_NONE;
3987 RunnerVisit[x][y] = 0;
3988 PlayerVisit[x][y] = 0;
3991 GfxRandom[x][y] = INIT_GFX_RANDOM();
3992 GfxElement[x][y] = EL_UNDEFINED;
3993 GfxAction[x][y] = ACTION_DEFAULT;
3994 GfxDir[x][y] = MV_NONE;
3995 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3998 SCAN_PLAYFIELD(x, y)
4000 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4002 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4004 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4007 InitField(x, y, TRUE);
4009 ResetGfxAnimation(x, y);
4014 for (i = 0; i < MAX_PLAYERS; i++)
4016 struct PlayerInfo *player = &stored_player[i];
4018 /* set number of special actions for bored and sleeping animation */
4019 player->num_special_action_bored =
4020 get_num_special_action(player->artwork_element,
4021 ACTION_BORING_1, ACTION_BORING_LAST);
4022 player->num_special_action_sleeping =
4023 get_num_special_action(player->artwork_element,
4024 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4027 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028 emulate_sb ? EMU_SOKOBAN :
4029 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4031 #if USE_NEW_ALL_SLIPPERY
4032 /* initialize type of slippery elements */
4033 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4035 if (!IS_CUSTOM_ELEMENT(i))
4037 /* default: elements slip down either to the left or right randomly */
4038 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4040 /* SP style elements prefer to slip down on the left side */
4041 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4042 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4044 /* BD style elements prefer to slip down on the left side */
4045 if (game.emulation == EMU_BOULDERDASH)
4046 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4051 /* initialize explosion and ignition delay */
4052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4054 if (!IS_CUSTOM_ELEMENT(i))
4057 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4058 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4059 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4060 int last_phase = (num_phase + 1) * delay;
4061 int half_phase = (num_phase / 2) * delay;
4063 element_info[i].explosion_delay = last_phase - 1;
4064 element_info[i].ignition_delay = half_phase;
4066 if (i == EL_BLACK_ORB)
4067 element_info[i].ignition_delay = 1;
4071 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4072 element_info[i].explosion_delay = 1;
4074 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4075 element_info[i].ignition_delay = 1;
4079 /* correct non-moving belts to start moving left */
4080 for (i = 0; i < NUM_BELTS; i++)
4081 if (game.belt_dir[i] == MV_NONE)
4082 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4084 #if USE_NEW_PLAYER_ASSIGNMENTS
4085 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4086 /* choose default local player */
4087 local_player = &stored_player[0];
4089 for (i = 0; i < MAX_PLAYERS; i++)
4090 stored_player[i].connected = FALSE;
4092 local_player->connected = TRUE;
4093 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
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 (tape.player_participates[i])
4102 stored_player[i].connected = TRUE;
4104 else if (setup.team_mode && !options.network)
4106 /* try to guess locally connected team mode players (needed for correct
4107 assignment of player figures from level to locally playing players) */
4109 for (i = 0; i < MAX_PLAYERS; i++)
4110 if (setup.input[i].use_joystick ||
4111 setup.input[i].key.left != KSYM_UNDEFINED)
4112 stored_player[i].connected = TRUE;
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 printf("::: player %d: %s\n", i,
4118 (stored_player[i].connected ? "connected" : "not connected"));
4120 for (i = 0; i < MAX_PLAYERS; i++)
4121 printf("::: player %d: %s\n", i,
4122 (stored_player[i].present ? "present" : "not present"));
4125 /* check if any connected player was not found in playfield */
4126 for (i = 0; i < MAX_PLAYERS; i++)
4128 struct PlayerInfo *player = &stored_player[i];
4130 if (player->connected && !player->present)
4132 struct PlayerInfo *field_player = NULL;
4135 printf("::: looking for field player for player %d ...\n", i);
4138 /* assign first free player found that is present in the playfield */
4140 /* first try: look for unmapped playfield player that is not connected */
4141 if (field_player == NULL)
4142 for (j = 0; j < MAX_PLAYERS; j++)
4143 if (stored_player[j].present &&
4144 !stored_player[j].mapped &&
4145 !stored_player[j].connected)
4146 field_player = &stored_player[j];
4148 /* second try: look for *any* unmapped playfield player */
4149 if (field_player == NULL)
4150 for (j = 0; j < MAX_PLAYERS; j++)
4151 if (stored_player[j].present &&
4152 !stored_player[j].mapped)
4153 field_player = &stored_player[j];
4155 if (field_player != NULL)
4157 int jx = field_player->jx, jy = field_player->jy;
4160 printf("::: found player figure %d\n", field_player->index_nr);
4163 player->present = FALSE;
4164 player->active = FALSE;
4166 field_player->present = TRUE;
4167 field_player->active = TRUE;
4170 player->initial_element = field_player->initial_element;
4171 player->artwork_element = field_player->artwork_element;
4173 player->block_last_field = field_player->block_last_field;
4174 player->block_delay_adjustment = field_player->block_delay_adjustment;
4177 StorePlayer[jx][jy] = field_player->element_nr;
4179 field_player->jx = field_player->last_jx = jx;
4180 field_player->jy = field_player->last_jy = jy;
4182 if (local_player == player)
4183 local_player = field_player;
4185 map_player_action[field_player->index_nr] = i;
4187 field_player->mapped = TRUE;
4190 printf("::: map_player_action[%d] == %d\n",
4191 field_player->index_nr, i);
4196 if (player->connected && player->present)
4197 player->mapped = TRUE;
4202 /* check if any connected player was not found in playfield */
4203 for (i = 0; i < MAX_PLAYERS; i++)
4205 struct PlayerInfo *player = &stored_player[i];
4207 if (player->connected && !player->present)
4209 for (j = 0; j < MAX_PLAYERS; j++)
4211 struct PlayerInfo *field_player = &stored_player[j];
4212 int jx = field_player->jx, jy = field_player->jy;
4214 /* assign first free player found that is present in the playfield */
4215 if (field_player->present && !field_player->connected)
4217 player->present = TRUE;
4218 player->active = TRUE;
4220 field_player->present = FALSE;
4221 field_player->active = FALSE;
4223 player->initial_element = field_player->initial_element;
4224 player->artwork_element = field_player->artwork_element;
4226 player->block_last_field = field_player->block_last_field;
4227 player->block_delay_adjustment = field_player->block_delay_adjustment;
4229 StorePlayer[jx][jy] = player->element_nr;
4231 player->jx = player->last_jx = jx;
4232 player->jy = player->last_jy = jy;
4242 printf("::: local_player->present == %d\n", local_player->present);
4247 /* when playing a tape, eliminate all players who do not participate */
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250 for (i = 0; i < MAX_PLAYERS; i++)
4252 if (stored_player[i].active &&
4253 !tape.player_participates[map_player_action[i]])
4255 struct PlayerInfo *player = &stored_player[i];
4256 int jx = player->jx, jy = player->jy;
4258 player->active = FALSE;
4259 StorePlayer[jx][jy] = 0;
4260 Feld[jx][jy] = EL_EMPTY;
4264 for (i = 0; i < MAX_PLAYERS; i++)
4266 if (stored_player[i].active &&
4267 !tape.player_participates[i])
4269 struct PlayerInfo *player = &stored_player[i];
4270 int jx = player->jx, jy = player->jy;
4272 player->active = FALSE;
4273 StorePlayer[jx][jy] = 0;
4274 Feld[jx][jy] = EL_EMPTY;
4279 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4281 /* when in single player mode, eliminate all but the first active player */
4283 for (i = 0; i < MAX_PLAYERS; i++)
4285 if (stored_player[i].active)
4287 for (j = i + 1; j < MAX_PLAYERS; j++)
4289 if (stored_player[j].active)
4291 struct PlayerInfo *player = &stored_player[j];
4292 int jx = player->jx, jy = player->jy;
4294 player->active = FALSE;
4295 player->present = FALSE;
4297 StorePlayer[jx][jy] = 0;
4298 Feld[jx][jy] = EL_EMPTY;
4305 /* when recording the game, store which players take part in the game */
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309 for (i = 0; i < MAX_PLAYERS; i++)
4310 if (stored_player[i].connected)
4311 tape.player_participates[i] = TRUE;
4313 for (i = 0; i < MAX_PLAYERS; i++)
4314 if (stored_player[i].active)
4315 tape.player_participates[i] = TRUE;
4321 for (i = 0; i < MAX_PLAYERS; i++)
4323 struct PlayerInfo *player = &stored_player[i];
4325 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4330 if (local_player == player)
4331 printf("Player %d is local player.\n", i+1);
4335 if (BorderElement == EL_EMPTY)
4338 SBX_Right = lev_fieldx - SCR_FIELDX;
4340 SBY_Lower = lev_fieldy - SCR_FIELDY;
4345 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4347 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4350 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4351 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4353 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4354 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4356 /* if local player not found, look for custom element that might create
4357 the player (make some assumptions about the right custom element) */
4358 if (!local_player->present)
4360 int start_x = 0, start_y = 0;
4361 int found_rating = 0;
4362 int found_element = EL_UNDEFINED;
4363 int player_nr = local_player->index_nr;
4365 SCAN_PLAYFIELD(x, y)
4367 int element = Feld[x][y];
4372 if (level.use_start_element[player_nr] &&
4373 level.start_element[player_nr] == element &&
4380 found_element = element;
4383 if (!IS_CUSTOM_ELEMENT(element))
4386 if (CAN_CHANGE(element))
4388 for (i = 0; i < element_info[element].num_change_pages; i++)
4390 /* check for player created from custom element as single target */
4391 content = element_info[element].change_page[i].target_element;
4392 is_player = ELEM_IS_PLAYER(content);
4394 if (is_player && (found_rating < 3 ||
4395 (found_rating == 3 && element < found_element)))
4401 found_element = element;
4406 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4408 /* check for player created from custom element as explosion content */
4409 content = element_info[element].content.e[xx][yy];
4410 is_player = ELEM_IS_PLAYER(content);
4412 if (is_player && (found_rating < 2 ||
4413 (found_rating == 2 && element < found_element)))
4415 start_x = x + xx - 1;
4416 start_y = y + yy - 1;
4419 found_element = element;
4422 if (!CAN_CHANGE(element))
4425 for (i = 0; i < element_info[element].num_change_pages; i++)
4427 /* check for player created from custom element as extended target */
4429 element_info[element].change_page[i].target_content.e[xx][yy];
4431 is_player = ELEM_IS_PLAYER(content);
4433 if (is_player && (found_rating < 1 ||
4434 (found_rating == 1 && element < found_element)))
4436 start_x = x + xx - 1;
4437 start_y = y + yy - 1;
4440 found_element = element;
4446 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4447 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4450 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4451 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4456 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4457 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4458 local_player->jx - MIDPOSX);
4460 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4461 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4462 local_player->jy - MIDPOSY);
4466 /* do not use PLAYING mask for fading out from main screen */
4467 game_status = GAME_MODE_MAIN;
4472 if (!game.restart_level)
4473 CloseDoor(DOOR_CLOSE_1);
4476 if (level_editor_test_game)
4477 FadeSkipNextFadeIn();
4479 FadeSetEnterScreen();
4481 if (level_editor_test_game)
4482 fading = fading_none;
4484 fading = menu.destination;
4488 FadeOut(REDRAW_FIELD);
4491 FadeOut(REDRAW_FIELD);
4495 game_status = GAME_MODE_PLAYING;
4498 /* !!! FIX THIS (START) !!! */
4499 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4501 InitGameEngine_EM();
4503 /* blit playfield from scroll buffer to normal back buffer for fading in */
4504 BlitScreenToBitmap_EM(backbuffer);
4506 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4508 InitGameEngine_SP();
4510 /* blit playfield from scroll buffer to normal back buffer for fading in */
4511 BlitScreenToBitmap_SP(backbuffer);
4518 /* after drawing the level, correct some elements */
4519 if (game.timegate_time_left == 0)
4520 CloseAllOpenTimegates();
4522 /* blit playfield from scroll buffer to normal back buffer for fading in */
4523 if (setup.soft_scrolling)
4524 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4526 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4528 /* !!! FIX THIS (END) !!! */
4531 FadeIn(REDRAW_FIELD);
4534 FadeIn(REDRAW_FIELD);
4539 if (!game.restart_level)
4541 /* copy default game door content to main double buffer */
4542 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4543 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4546 SetPanelBackground();
4547 SetDrawBackgroundMask(REDRAW_DOOR_1);
4550 UpdateAndDisplayGameControlValues();
4552 UpdateGameDoorValues();
4553 DrawGameDoorValues();
4556 if (!game.restart_level)
4560 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4561 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4562 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4566 /* copy actual game door content to door double buffer for OpenDoor() */
4567 BlitBitmap(drawto, bitmap_db_door,
4568 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4570 OpenDoor(DOOR_OPEN_ALL);
4572 PlaySound(SND_GAME_STARTING);
4574 if (setup.sound_music)
4577 KeyboardAutoRepeatOffUnlessAutoplay();
4581 for (i = 0; i < MAX_PLAYERS; i++)
4582 printf("Player %d %sactive.\n",
4583 i + 1, (stored_player[i].active ? "" : "not "));
4594 game.restart_level = FALSE;
4597 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4599 /* this is used for non-R'n'D game engines to update certain engine values */
4601 /* needed to determine if sounds are played within the visible screen area */
4602 scroll_x = actual_scroll_x;
4603 scroll_y = actual_scroll_y;
4606 void InitMovDir(int x, int y)
4608 int i, element = Feld[x][y];
4609 static int xy[4][2] =
4616 static int direction[3][4] =
4618 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4619 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4620 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4629 Feld[x][y] = EL_BUG;
4630 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4633 case EL_SPACESHIP_RIGHT:
4634 case EL_SPACESHIP_UP:
4635 case EL_SPACESHIP_LEFT:
4636 case EL_SPACESHIP_DOWN:
4637 Feld[x][y] = EL_SPACESHIP;
4638 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4641 case EL_BD_BUTTERFLY_RIGHT:
4642 case EL_BD_BUTTERFLY_UP:
4643 case EL_BD_BUTTERFLY_LEFT:
4644 case EL_BD_BUTTERFLY_DOWN:
4645 Feld[x][y] = EL_BD_BUTTERFLY;
4646 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4649 case EL_BD_FIREFLY_RIGHT:
4650 case EL_BD_FIREFLY_UP:
4651 case EL_BD_FIREFLY_LEFT:
4652 case EL_BD_FIREFLY_DOWN:
4653 Feld[x][y] = EL_BD_FIREFLY;
4654 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4657 case EL_PACMAN_RIGHT:
4659 case EL_PACMAN_LEFT:
4660 case EL_PACMAN_DOWN:
4661 Feld[x][y] = EL_PACMAN;
4662 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4665 case EL_YAMYAM_LEFT:
4666 case EL_YAMYAM_RIGHT:
4668 case EL_YAMYAM_DOWN:
4669 Feld[x][y] = EL_YAMYAM;
4670 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4673 case EL_SP_SNIKSNAK:
4674 MovDir[x][y] = MV_UP;
4677 case EL_SP_ELECTRON:
4678 MovDir[x][y] = MV_LEFT;
4685 Feld[x][y] = EL_MOLE;
4686 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4690 if (IS_CUSTOM_ELEMENT(element))
4692 struct ElementInfo *ei = &element_info[element];
4693 int move_direction_initial = ei->move_direction_initial;
4694 int move_pattern = ei->move_pattern;
4696 if (move_direction_initial == MV_START_PREVIOUS)
4698 if (MovDir[x][y] != MV_NONE)
4701 move_direction_initial = MV_START_AUTOMATIC;
4704 if (move_direction_initial == MV_START_RANDOM)
4705 MovDir[x][y] = 1 << RND(4);
4706 else if (move_direction_initial & MV_ANY_DIRECTION)
4707 MovDir[x][y] = move_direction_initial;
4708 else if (move_pattern == MV_ALL_DIRECTIONS ||
4709 move_pattern == MV_TURNING_LEFT ||
4710 move_pattern == MV_TURNING_RIGHT ||
4711 move_pattern == MV_TURNING_LEFT_RIGHT ||
4712 move_pattern == MV_TURNING_RIGHT_LEFT ||
4713 move_pattern == MV_TURNING_RANDOM)
4714 MovDir[x][y] = 1 << RND(4);
4715 else if (move_pattern == MV_HORIZONTAL)
4716 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4717 else if (move_pattern == MV_VERTICAL)
4718 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4719 else if (move_pattern & MV_ANY_DIRECTION)
4720 MovDir[x][y] = element_info[element].move_pattern;
4721 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4722 move_pattern == MV_ALONG_RIGHT_SIDE)
4724 /* use random direction as default start direction */
4725 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4726 MovDir[x][y] = 1 << RND(4);
4728 for (i = 0; i < NUM_DIRECTIONS; i++)
4730 int x1 = x + xy[i][0];
4731 int y1 = y + xy[i][1];
4733 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4735 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4736 MovDir[x][y] = direction[0][i];
4738 MovDir[x][y] = direction[1][i];
4747 MovDir[x][y] = 1 << RND(4);
4749 if (element != EL_BUG &&
4750 element != EL_SPACESHIP &&
4751 element != EL_BD_BUTTERFLY &&
4752 element != EL_BD_FIREFLY)
4755 for (i = 0; i < NUM_DIRECTIONS; i++)
4757 int x1 = x + xy[i][0];
4758 int y1 = y + xy[i][1];
4760 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4762 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4764 MovDir[x][y] = direction[0][i];
4767 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4768 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4770 MovDir[x][y] = direction[1][i];
4779 GfxDir[x][y] = MovDir[x][y];
4782 void InitAmoebaNr(int x, int y)
4785 int group_nr = AmoebeNachbarNr(x, y);
4789 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4791 if (AmoebaCnt[i] == 0)
4799 AmoebaNr[x][y] = group_nr;
4800 AmoebaCnt[group_nr]++;
4801 AmoebaCnt2[group_nr]++;
4804 static void PlayerWins(struct PlayerInfo *player)
4806 player->LevelSolved = TRUE;
4807 player->GameOver = TRUE;
4809 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4810 level.native_em_level->lev->score : player->score);
4812 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4813 player->LevelSolved_CountingScore = player->score_final;
4818 static int time, time_final;
4819 static int score, score_final;
4820 static int game_over_delay_1 = 0;
4821 static int game_over_delay_2 = 0;
4822 int game_over_delay_value_1 = 50;
4823 int game_over_delay_value_2 = 50;
4825 if (!local_player->LevelSolved_GameWon)
4829 /* do not start end game actions before the player stops moving (to exit) */
4830 if (local_player->MovPos)
4833 local_player->LevelSolved_GameWon = TRUE;
4834 local_player->LevelSolved_SaveTape = tape.recording;
4835 local_player->LevelSolved_SaveScore = !tape.playing;
4837 if (tape.auto_play) /* tape might already be stopped here */
4838 tape.auto_play_level_solved = TRUE;
4844 game_over_delay_1 = game_over_delay_value_1;
4845 game_over_delay_2 = game_over_delay_value_2;
4847 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4848 score = score_final = local_player->score_final;
4853 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4855 else if (level.time == 0 && TimePlayed < 999)
4858 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4861 local_player->score_final = score_final;
4863 if (level_editor_test_game)
4866 score = score_final;
4869 local_player->LevelSolved_CountingTime = time;
4870 local_player->LevelSolved_CountingScore = score;
4872 game_panel_controls[GAME_PANEL_TIME].value = time;
4873 game_panel_controls[GAME_PANEL_SCORE].value = score;
4875 DisplayGameControlValues();
4877 DrawGameValue_Time(time);
4878 DrawGameValue_Score(score);
4882 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4884 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4886 /* close exit door after last player */
4887 if ((AllPlayersGone &&
4888 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4889 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4890 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4891 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4892 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4894 int element = Feld[ExitX][ExitY];
4897 if (element == EL_EM_EXIT_OPEN ||
4898 element == EL_EM_STEEL_EXIT_OPEN)
4905 Feld[ExitX][ExitY] =
4906 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4907 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4908 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4909 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4910 EL_EM_STEEL_EXIT_CLOSING);
4912 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4916 /* player disappears */
4917 DrawLevelField(ExitX, ExitY);
4920 for (i = 0; i < MAX_PLAYERS; i++)
4922 struct PlayerInfo *player = &stored_player[i];
4924 if (player->present)
4926 RemovePlayer(player);
4928 /* player disappears */
4929 DrawLevelField(player->jx, player->jy);
4934 PlaySound(SND_GAME_WINNING);
4937 if (game_over_delay_1 > 0)
4939 game_over_delay_1--;
4944 if (time != time_final)
4946 int time_to_go = ABS(time_final - time);
4947 int time_count_dir = (time < time_final ? +1 : -1);
4948 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4950 time += time_count_steps * time_count_dir;
4951 score += time_count_steps * level.score[SC_TIME_BONUS];
4954 local_player->LevelSolved_CountingTime = time;
4955 local_player->LevelSolved_CountingScore = score;
4957 game_panel_controls[GAME_PANEL_TIME].value = time;
4958 game_panel_controls[GAME_PANEL_SCORE].value = score;
4960 DisplayGameControlValues();
4962 DrawGameValue_Time(time);
4963 DrawGameValue_Score(score);
4966 if (time == time_final)
4967 StopSound(SND_GAME_LEVELTIME_BONUS);
4968 else if (setup.sound_loops)
4969 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4971 PlaySound(SND_GAME_LEVELTIME_BONUS);
4976 local_player->LevelSolved_PanelOff = TRUE;
4978 if (game_over_delay_2 > 0)
4980 game_over_delay_2--;
4993 boolean raise_level = FALSE;
4995 local_player->LevelSolved_GameEnd = TRUE;
4997 CloseDoor(DOOR_CLOSE_1);
4999 if (local_player->LevelSolved_SaveTape)
5006 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5008 SaveTape(tape.level_nr); /* ask to save tape */
5012 if (level_editor_test_game)
5014 game_status = GAME_MODE_MAIN;
5017 DrawAndFadeInMainMenu(REDRAW_FIELD);
5025 if (!local_player->LevelSolved_SaveScore)
5028 FadeOut(REDRAW_FIELD);
5031 game_status = GAME_MODE_MAIN;
5033 DrawAndFadeInMainMenu(REDRAW_FIELD);
5038 if (level_nr == leveldir_current->handicap_level)
5040 leveldir_current->handicap_level++;
5041 SaveLevelSetup_SeriesInfo();
5044 if (level_nr < leveldir_current->last_level)
5045 raise_level = TRUE; /* advance to next level */
5047 if ((hi_pos = NewHiScore()) >= 0)
5049 game_status = GAME_MODE_SCORES;
5051 DrawHallOfFame(hi_pos);
5062 FadeOut(REDRAW_FIELD);
5065 game_status = GAME_MODE_MAIN;
5073 DrawAndFadeInMainMenu(REDRAW_FIELD);
5082 LoadScore(level_nr);
5084 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5085 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5088 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5090 if (local_player->score_final > highscore[k].Score)
5092 /* player has made it to the hall of fame */
5094 if (k < MAX_SCORE_ENTRIES - 1)
5096 int m = MAX_SCORE_ENTRIES - 1;
5099 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5100 if (strEqual(setup.player_name, highscore[l].Name))
5102 if (m == k) /* player's new highscore overwrites his old one */
5106 for (l = m; l > k; l--)
5108 strcpy(highscore[l].Name, highscore[l - 1].Name);
5109 highscore[l].Score = highscore[l - 1].Score;
5116 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5117 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5118 highscore[k].Score = local_player->score_final;
5124 else if (!strncmp(setup.player_name, highscore[k].Name,
5125 MAX_PLAYER_NAME_LEN))
5126 break; /* player already there with a higher score */
5132 SaveScore(level_nr);
5137 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5139 int element = Feld[x][y];
5140 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5141 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5142 int horiz_move = (dx != 0);
5143 int sign = (horiz_move ? dx : dy);
5144 int step = sign * element_info[element].move_stepsize;
5146 /* special values for move stepsize for spring and things on conveyor belt */
5149 if (CAN_FALL(element) &&
5150 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5151 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5152 else if (element == EL_SPRING)
5153 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5159 inline static int getElementMoveStepsize(int x, int y)
5161 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5164 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5166 if (player->GfxAction != action || player->GfxDir != dir)
5169 printf("Player frame reset! (%d => %d, %d => %d)\n",
5170 player->GfxAction, action, player->GfxDir, dir);
5173 player->GfxAction = action;
5174 player->GfxDir = dir;
5176 player->StepFrame = 0;
5180 #if USE_GFX_RESET_GFX_ANIMATION
5181 static void ResetGfxFrame(int x, int y, boolean redraw)
5183 int element = Feld[x][y];
5184 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5185 int last_gfx_frame = GfxFrame[x][y];
5187 if (graphic_info[graphic].anim_global_sync)
5188 GfxFrame[x][y] = FrameCounter;
5189 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5190 GfxFrame[x][y] = CustomValue[x][y];
5191 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5192 GfxFrame[x][y] = element_info[element].collect_score;
5193 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5194 GfxFrame[x][y] = ChangeDelay[x][y];
5196 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5197 DrawLevelGraphicAnimation(x, y, graphic);
5201 static void ResetGfxAnimation(int x, int y)
5203 GfxAction[x][y] = ACTION_DEFAULT;
5204 GfxDir[x][y] = MovDir[x][y];
5207 #if USE_GFX_RESET_GFX_ANIMATION
5208 ResetGfxFrame(x, y, FALSE);
5212 static void ResetRandomAnimationValue(int x, int y)
5214 GfxRandom[x][y] = INIT_GFX_RANDOM();
5217 void InitMovingField(int x, int y, int direction)
5219 int element = Feld[x][y];
5220 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5221 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5224 boolean is_moving_before, is_moving_after;
5226 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5229 /* check if element was/is moving or being moved before/after mode change */
5232 is_moving_before = (WasJustMoving[x][y] != 0);
5234 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5235 is_moving_before = WasJustMoving[x][y];
5238 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5240 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5242 /* reset animation only for moving elements which change direction of moving
5243 or which just started or stopped moving
5244 (else CEs with property "can move" / "not moving" are reset each frame) */
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5247 if (is_moving_before != is_moving_after ||
5248 direction != MovDir[x][y])
5249 ResetGfxAnimation(x, y);
5251 if ((is_moving_before || is_moving_after) && !continues_moving)
5252 ResetGfxAnimation(x, y);
5255 if (!continues_moving)
5256 ResetGfxAnimation(x, y);
5259 MovDir[x][y] = direction;
5260 GfxDir[x][y] = direction;
5262 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5263 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5264 direction == MV_DOWN && CAN_FALL(element) ?
5265 ACTION_FALLING : ACTION_MOVING);
5267 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5268 ACTION_FALLING : ACTION_MOVING);
5271 /* this is needed for CEs with property "can move" / "not moving" */
5273 if (is_moving_after)
5275 if (Feld[newx][newy] == EL_EMPTY)
5276 Feld[newx][newy] = EL_BLOCKED;
5278 MovDir[newx][newy] = MovDir[x][y];
5280 #if USE_NEW_CUSTOM_VALUE
5281 CustomValue[newx][newy] = CustomValue[x][y];
5284 GfxFrame[newx][newy] = GfxFrame[x][y];
5285 GfxRandom[newx][newy] = GfxRandom[x][y];
5286 GfxAction[newx][newy] = GfxAction[x][y];
5287 GfxDir[newx][newy] = GfxDir[x][y];
5291 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5293 int direction = MovDir[x][y];
5294 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5295 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5301 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5303 int oldx = x, oldy = y;
5304 int direction = MovDir[x][y];
5306 if (direction == MV_LEFT)
5308 else if (direction == MV_RIGHT)
5310 else if (direction == MV_UP)
5312 else if (direction == MV_DOWN)
5315 *comes_from_x = oldx;
5316 *comes_from_y = oldy;
5319 int MovingOrBlocked2Element(int x, int y)
5321 int element = Feld[x][y];
5323 if (element == EL_BLOCKED)
5327 Blocked2Moving(x, y, &oldx, &oldy);
5328 return Feld[oldx][oldy];
5334 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5336 /* like MovingOrBlocked2Element(), but if element is moving
5337 and (x,y) is the field the moving element is just leaving,
5338 return EL_BLOCKED instead of the element value */
5339 int element = Feld[x][y];
5341 if (IS_MOVING(x, y))
5343 if (element == EL_BLOCKED)
5347 Blocked2Moving(x, y, &oldx, &oldy);
5348 return Feld[oldx][oldy];
5357 static void RemoveField(int x, int y)
5359 Feld[x][y] = EL_EMPTY;
5365 #if USE_NEW_CUSTOM_VALUE
5366 CustomValue[x][y] = 0;
5370 ChangeDelay[x][y] = 0;
5371 ChangePage[x][y] = -1;
5372 Pushed[x][y] = FALSE;
5375 ExplodeField[x][y] = EX_TYPE_NONE;
5378 GfxElement[x][y] = EL_UNDEFINED;
5379 GfxAction[x][y] = ACTION_DEFAULT;
5380 GfxDir[x][y] = MV_NONE;
5382 /* !!! this would prevent the removed tile from being redrawn !!! */
5383 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5387 void RemoveMovingField(int x, int y)
5389 int oldx = x, oldy = y, newx = x, newy = y;
5390 int element = Feld[x][y];
5391 int next_element = EL_UNDEFINED;
5393 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5396 if (IS_MOVING(x, y))
5398 Moving2Blocked(x, y, &newx, &newy);
5400 if (Feld[newx][newy] != EL_BLOCKED)
5402 /* element is moving, but target field is not free (blocked), but
5403 already occupied by something different (example: acid pool);
5404 in this case, only remove the moving field, but not the target */
5406 RemoveField(oldx, oldy);
5408 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5410 TEST_DrawLevelField(oldx, oldy);
5415 else if (element == EL_BLOCKED)
5417 Blocked2Moving(x, y, &oldx, &oldy);
5418 if (!IS_MOVING(oldx, oldy))
5422 if (element == EL_BLOCKED &&
5423 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5424 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5425 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5426 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5427 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5428 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5429 next_element = get_next_element(Feld[oldx][oldy]);
5431 RemoveField(oldx, oldy);
5432 RemoveField(newx, newy);
5434 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5436 if (next_element != EL_UNDEFINED)
5437 Feld[oldx][oldy] = next_element;
5439 TEST_DrawLevelField(oldx, oldy);
5440 TEST_DrawLevelField(newx, newy);
5443 void DrawDynamite(int x, int y)
5445 int sx = SCREENX(x), sy = SCREENY(y);
5446 int graphic = el2img(Feld[x][y]);
5449 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5452 if (IS_WALKABLE_INSIDE(Back[x][y]))
5456 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5457 else if (Store[x][y])
5458 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5460 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5462 if (Back[x][y] || Store[x][y])
5463 DrawGraphicThruMask(sx, sy, graphic, frame);
5465 DrawGraphic(sx, sy, graphic, frame);
5468 void CheckDynamite(int x, int y)
5470 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5474 if (MovDelay[x][y] != 0)
5477 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5483 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5488 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5490 boolean num_checked_players = 0;
5493 for (i = 0; i < MAX_PLAYERS; i++)
5495 if (stored_player[i].active)
5497 int sx = stored_player[i].jx;
5498 int sy = stored_player[i].jy;
5500 if (num_checked_players == 0)
5507 *sx1 = MIN(*sx1, sx);
5508 *sy1 = MIN(*sy1, sy);
5509 *sx2 = MAX(*sx2, sx);
5510 *sy2 = MAX(*sy2, sy);
5513 num_checked_players++;
5518 static boolean checkIfAllPlayersFitToScreen_RND()
5520 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5522 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5524 return (sx2 - sx1 < SCR_FIELDX &&
5525 sy2 - sy1 < SCR_FIELDY);
5528 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5530 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5532 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5534 *sx = (sx1 + sx2) / 2;
5535 *sy = (sy1 + sy2) / 2;
5538 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5539 boolean center_screen, boolean quick_relocation)
5541 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5542 boolean no_delay = (tape.warp_forward);
5543 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5544 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5546 if (quick_relocation)
5548 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5550 if (!level.shifted_relocation || center_screen)
5552 /* quick relocation (without scrolling), with centering of screen */
5554 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5555 x > SBX_Right + MIDPOSX ? SBX_Right :
5558 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5559 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5564 /* quick relocation (without scrolling), but do not center screen */
5566 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5567 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5570 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5574 int offset_x = x + (scroll_x - center_scroll_x);
5575 int offset_y = y + (scroll_y - center_scroll_y);
5577 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5578 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5579 offset_x - MIDPOSX);
5581 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583 offset_y - MIDPOSY);
5589 if (!level.shifted_relocation || center_screen)
5591 /* quick relocation (without scrolling), with centering of screen */
5593 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5594 x > SBX_Right + MIDPOSX ? SBX_Right :
5597 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5598 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5603 /* quick relocation (without scrolling), but do not center screen */
5605 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5606 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5609 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5613 int offset_x = x + (scroll_x - center_scroll_x);
5614 int offset_y = y + (scroll_y - center_scroll_y);
5616 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5617 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5618 offset_x - MIDPOSX);
5620 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622 offset_y - MIDPOSY);
5625 /* quick relocation (without scrolling), inside visible screen area */
5627 int offset = game.scroll_delay_value;
5629 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5630 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5631 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5633 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5634 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5635 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5637 /* don't scroll over playfield boundaries */
5638 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5639 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5641 /* don't scroll over playfield boundaries */
5642 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5643 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5647 RedrawPlayfield(TRUE, 0,0,0,0);
5652 int scroll_xx, scroll_yy;
5654 if (!level.shifted_relocation || center_screen)
5656 /* visible relocation (with scrolling), with centering of screen */
5658 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5659 x > SBX_Right + MIDPOSX ? SBX_Right :
5662 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5663 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668 /* visible relocation (with scrolling), but do not center screen */
5670 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5671 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5674 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5678 int offset_x = x + (scroll_x - center_scroll_x);
5679 int offset_y = y + (scroll_y - center_scroll_y);
5681 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5682 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5683 offset_x - MIDPOSX);
5685 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5687 offset_y - MIDPOSY);
5692 /* visible relocation (with scrolling), with centering of screen */
5694 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5695 x > SBX_Right + MIDPOSX ? SBX_Right :
5698 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5699 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5703 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5705 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5708 int fx = FX, fy = FY;
5710 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5711 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5713 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5719 fx += dx * TILEX / 2;
5720 fy += dy * TILEY / 2;
5722 ScrollLevel(dx, dy);
5725 /* scroll in two steps of half tile size to make things smoother */
5726 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5728 Delay(wait_delay_value);
5730 /* scroll second step to align at full tile size */
5732 Delay(wait_delay_value);
5737 Delay(wait_delay_value);
5741 void RelocatePlayer(int jx, int jy, int el_player_raw)
5743 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5744 int player_nr = GET_PLAYER_NR(el_player);
5745 struct PlayerInfo *player = &stored_player[player_nr];
5746 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5747 boolean no_delay = (tape.warp_forward);
5748 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5749 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5750 int old_jx = player->jx;
5751 int old_jy = player->jy;
5752 int old_element = Feld[old_jx][old_jy];
5753 int element = Feld[jx][jy];
5754 boolean player_relocated = (old_jx != jx || old_jy != jy);
5756 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5757 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5758 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5759 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5760 int leave_side_horiz = move_dir_horiz;
5761 int leave_side_vert = move_dir_vert;
5762 int enter_side = enter_side_horiz | enter_side_vert;
5763 int leave_side = leave_side_horiz | leave_side_vert;
5765 if (player->GameOver) /* do not reanimate dead player */
5768 if (!player_relocated) /* no need to relocate the player */
5771 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5773 RemoveField(jx, jy); /* temporarily remove newly placed player */
5774 DrawLevelField(jx, jy);
5777 if (player->present)
5779 while (player->MovPos)
5781 ScrollPlayer(player, SCROLL_GO_ON);
5782 ScrollScreen(NULL, SCROLL_GO_ON);
5784 AdvanceFrameAndPlayerCounters(player->index_nr);
5789 Delay(wait_delay_value);
5792 DrawPlayer(player); /* needed here only to cleanup last field */
5793 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5795 player->is_moving = FALSE;
5798 if (IS_CUSTOM_ELEMENT(old_element))
5799 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5801 player->index_bit, leave_side);
5803 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5805 player->index_bit, leave_side);
5807 Feld[jx][jy] = el_player;
5808 InitPlayerField(jx, jy, el_player, TRUE);
5810 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5811 possible that the relocation target field did not contain a player element,
5812 but a walkable element, to which the new player was relocated -- in this
5813 case, restore that (already initialized!) element on the player field */
5814 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5816 Feld[jx][jy] = element; /* restore previously existing element */
5818 /* !!! do not initialize already initialized element a second time !!! */
5819 /* (this causes at least problems with "element creation" CE trigger for
5820 already existing elements, and existing Sokoban fields counted twice) */
5821 InitField(jx, jy, FALSE);
5825 /* only visually relocate centered player */
5826 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5827 FALSE, level.instant_relocation);
5829 TestIfPlayerTouchesBadThing(jx, jy);
5830 TestIfPlayerTouchesCustomElement(jx, jy);
5832 if (IS_CUSTOM_ELEMENT(element))
5833 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5834 player->index_bit, enter_side);
5836 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5837 player->index_bit, enter_side);
5840 if (player->is_switching)
5842 /* ensure that relocation while still switching an element does not cause
5843 a new element to be treated as also switched directly after relocation
5844 (this is important for teleporter switches that teleport the player to
5845 a place where another teleporter switch is in the same direction, which
5846 would then incorrectly be treated as immediately switched before the
5847 direction key that caused the switch was released) */
5849 player->switch_x += jx - old_jx;
5850 player->switch_y += jy - old_jy;
5855 void Explode(int ex, int ey, int phase, int mode)
5861 /* !!! eliminate this variable !!! */
5862 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5864 if (game.explosions_delayed)
5866 ExplodeField[ex][ey] = mode;
5870 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5872 int center_element = Feld[ex][ey];
5873 int artwork_element, explosion_element; /* set these values later */
5876 /* --- This is only really needed (and now handled) in "Impact()". --- */
5877 /* do not explode moving elements that left the explode field in time */
5878 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5879 center_element == EL_EMPTY &&
5880 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5885 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5886 if (mode == EX_TYPE_NORMAL ||
5887 mode == EX_TYPE_CENTER ||
5888 mode == EX_TYPE_CROSS)
5889 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5892 /* remove things displayed in background while burning dynamite */
5893 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5896 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5898 /* put moving element to center field (and let it explode there) */
5899 center_element = MovingOrBlocked2Element(ex, ey);
5900 RemoveMovingField(ex, ey);
5901 Feld[ex][ey] = center_element;
5904 /* now "center_element" is finally determined -- set related values now */
5905 artwork_element = center_element; /* for custom player artwork */
5906 explosion_element = center_element; /* for custom player artwork */
5908 if (IS_PLAYER(ex, ey))
5910 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5912 artwork_element = stored_player[player_nr].artwork_element;
5914 if (level.use_explosion_element[player_nr])
5916 explosion_element = level.explosion_element[player_nr];
5917 artwork_element = explosion_element;
5922 if (mode == EX_TYPE_NORMAL ||
5923 mode == EX_TYPE_CENTER ||
5924 mode == EX_TYPE_CROSS)
5925 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5928 last_phase = element_info[explosion_element].explosion_delay + 1;
5930 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5932 int xx = x - ex + 1;
5933 int yy = y - ey + 1;
5936 if (!IN_LEV_FIELD(x, y) ||
5937 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5938 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5941 element = Feld[x][y];
5943 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5945 element = MovingOrBlocked2Element(x, y);
5947 if (!IS_EXPLOSION_PROOF(element))
5948 RemoveMovingField(x, y);
5951 /* indestructible elements can only explode in center (but not flames) */
5952 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5953 mode == EX_TYPE_BORDER)) ||
5954 element == EL_FLAMES)
5957 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5958 behaviour, for example when touching a yamyam that explodes to rocks
5959 with active deadly shield, a rock is created under the player !!! */
5960 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5962 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5963 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5964 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5966 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5969 if (IS_ACTIVE_BOMB(element))
5971 /* re-activate things under the bomb like gate or penguin */
5972 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5979 /* save walkable background elements while explosion on same tile */
5980 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5981 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5982 Back[x][y] = element;
5984 /* ignite explodable elements reached by other explosion */
5985 if (element == EL_EXPLOSION)
5986 element = Store2[x][y];
5988 if (AmoebaNr[x][y] &&
5989 (element == EL_AMOEBA_FULL ||
5990 element == EL_BD_AMOEBA ||
5991 element == EL_AMOEBA_GROWING))
5993 AmoebaCnt[AmoebaNr[x][y]]--;
5994 AmoebaCnt2[AmoebaNr[x][y]]--;
5999 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6001 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6003 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6005 if (PLAYERINFO(ex, ey)->use_murphy)
6006 Store[x][y] = EL_EMPTY;
6009 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6010 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6011 else if (ELEM_IS_PLAYER(center_element))
6012 Store[x][y] = EL_EMPTY;
6013 else if (center_element == EL_YAMYAM)
6014 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6015 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6016 Store[x][y] = element_info[center_element].content.e[xx][yy];
6018 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6019 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6020 otherwise) -- FIX THIS !!! */
6021 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6022 Store[x][y] = element_info[element].content.e[1][1];
6024 else if (!CAN_EXPLODE(element))
6025 Store[x][y] = element_info[element].content.e[1][1];
6028 Store[x][y] = EL_EMPTY;
6030 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6031 center_element == EL_AMOEBA_TO_DIAMOND)
6032 Store2[x][y] = element;
6034 Feld[x][y] = EL_EXPLOSION;
6035 GfxElement[x][y] = artwork_element;
6037 ExplodePhase[x][y] = 1;
6038 ExplodeDelay[x][y] = last_phase;
6043 if (center_element == EL_YAMYAM)
6044 game.yamyam_content_nr =
6045 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6057 GfxFrame[x][y] = 0; /* restart explosion animation */
6059 last_phase = ExplodeDelay[x][y];
6061 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6065 /* activate this even in non-DEBUG version until cause for crash in
6066 getGraphicAnimationFrame() (see below) is found and eliminated */
6072 /* this can happen if the player leaves an explosion just in time */
6073 if (GfxElement[x][y] == EL_UNDEFINED)
6074 GfxElement[x][y] = EL_EMPTY;
6076 if (GfxElement[x][y] == EL_UNDEFINED)
6079 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6080 printf("Explode(): This should never happen!\n");
6083 GfxElement[x][y] = EL_EMPTY;
6089 border_element = Store2[x][y];
6090 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6091 border_element = StorePlayer[x][y];
6093 if (phase == element_info[border_element].ignition_delay ||
6094 phase == last_phase)
6096 boolean border_explosion = FALSE;
6098 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6099 !PLAYER_EXPLOSION_PROTECTED(x, y))
6101 KillPlayerUnlessExplosionProtected(x, y);
6102 border_explosion = TRUE;
6104 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6106 Feld[x][y] = Store2[x][y];
6109 border_explosion = TRUE;
6111 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6113 AmoebeUmwandeln(x, y);
6115 border_explosion = TRUE;
6118 /* if an element just explodes due to another explosion (chain-reaction),
6119 do not immediately end the new explosion when it was the last frame of
6120 the explosion (as it would be done in the following "if"-statement!) */
6121 if (border_explosion && phase == last_phase)
6125 if (phase == last_phase)
6129 element = Feld[x][y] = Store[x][y];
6130 Store[x][y] = Store2[x][y] = 0;
6131 GfxElement[x][y] = EL_UNDEFINED;
6133 /* player can escape from explosions and might therefore be still alive */
6134 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6135 element <= EL_PLAYER_IS_EXPLODING_4)
6137 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6138 int explosion_element = EL_PLAYER_1 + player_nr;
6139 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6140 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6142 if (level.use_explosion_element[player_nr])
6143 explosion_element = level.explosion_element[player_nr];
6145 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6146 element_info[explosion_element].content.e[xx][yy]);
6149 /* restore probably existing indestructible background element */
6150 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6151 element = Feld[x][y] = Back[x][y];
6154 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6155 GfxDir[x][y] = MV_NONE;
6156 ChangeDelay[x][y] = 0;
6157 ChangePage[x][y] = -1;
6159 #if USE_NEW_CUSTOM_VALUE
6160 CustomValue[x][y] = 0;
6163 InitField_WithBug2(x, y, FALSE);
6165 TEST_DrawLevelField(x, y);
6167 TestIfElementTouchesCustomElement(x, y);
6169 if (GFX_CRUMBLED(element))
6170 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6172 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6173 StorePlayer[x][y] = 0;
6175 if (ELEM_IS_PLAYER(element))
6176 RelocatePlayer(x, y, element);
6178 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6180 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6181 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6184 TEST_DrawLevelFieldCrumbled(x, y);
6186 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6188 DrawLevelElement(x, y, Back[x][y]);
6189 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6191 else if (IS_WALKABLE_UNDER(Back[x][y]))
6193 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6194 DrawLevelElementThruMask(x, y, Back[x][y]);
6196 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6197 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6201 void DynaExplode(int ex, int ey)
6204 int dynabomb_element = Feld[ex][ey];
6205 int dynabomb_size = 1;
6206 boolean dynabomb_xl = FALSE;
6207 struct PlayerInfo *player;
6208 static int xy[4][2] =
6216 if (IS_ACTIVE_BOMB(dynabomb_element))
6218 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6219 dynabomb_size = player->dynabomb_size;
6220 dynabomb_xl = player->dynabomb_xl;
6221 player->dynabombs_left++;
6224 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6226 for (i = 0; i < NUM_DIRECTIONS; i++)
6228 for (j = 1; j <= dynabomb_size; j++)
6230 int x = ex + j * xy[i][0];
6231 int y = ey + j * xy[i][1];
6234 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6237 element = Feld[x][y];
6239 /* do not restart explosions of fields with active bombs */
6240 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6243 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6245 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6246 !IS_DIGGABLE(element) && !dynabomb_xl)
6252 void Bang(int x, int y)
6254 int element = MovingOrBlocked2Element(x, y);
6255 int explosion_type = EX_TYPE_NORMAL;
6257 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6259 struct PlayerInfo *player = PLAYERINFO(x, y);
6261 #if USE_FIX_CE_ACTION_WITH_PLAYER
6262 element = Feld[x][y] = player->initial_element;
6264 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6265 player->element_nr);
6268 if (level.use_explosion_element[player->index_nr])
6270 int explosion_element = level.explosion_element[player->index_nr];
6272 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6273 explosion_type = EX_TYPE_CROSS;
6274 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6275 explosion_type = EX_TYPE_CENTER;
6283 case EL_BD_BUTTERFLY:
6286 case EL_DARK_YAMYAM:
6290 RaiseScoreElement(element);
6293 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6294 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6295 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6296 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6297 case EL_DYNABOMB_INCREASE_NUMBER:
6298 case EL_DYNABOMB_INCREASE_SIZE:
6299 case EL_DYNABOMB_INCREASE_POWER:
6300 explosion_type = EX_TYPE_DYNA;
6303 case EL_DC_LANDMINE:
6305 case EL_EM_EXIT_OPEN:
6306 case EL_EM_STEEL_EXIT_OPEN:
6308 explosion_type = EX_TYPE_CENTER;
6313 case EL_LAMP_ACTIVE:
6314 case EL_AMOEBA_TO_DIAMOND:
6315 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6316 explosion_type = EX_TYPE_CENTER;
6320 if (element_info[element].explosion_type == EXPLODES_CROSS)
6321 explosion_type = EX_TYPE_CROSS;
6322 else if (element_info[element].explosion_type == EXPLODES_1X1)
6323 explosion_type = EX_TYPE_CENTER;
6327 if (explosion_type == EX_TYPE_DYNA)
6330 Explode(x, y, EX_PHASE_START, explosion_type);
6332 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6335 void SplashAcid(int x, int y)
6337 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6338 (!IN_LEV_FIELD(x - 1, y - 2) ||
6339 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6340 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6342 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6343 (!IN_LEV_FIELD(x + 1, y - 2) ||
6344 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6345 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6347 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6350 static void InitBeltMovement()
6352 static int belt_base_element[4] =
6354 EL_CONVEYOR_BELT_1_LEFT,
6355 EL_CONVEYOR_BELT_2_LEFT,
6356 EL_CONVEYOR_BELT_3_LEFT,
6357 EL_CONVEYOR_BELT_4_LEFT
6359 static int belt_base_active_element[4] =
6361 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6362 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6363 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6364 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6369 /* set frame order for belt animation graphic according to belt direction */
6370 for (i = 0; i < NUM_BELTS; i++)
6374 for (j = 0; j < NUM_BELT_PARTS; j++)
6376 int element = belt_base_active_element[belt_nr] + j;
6377 int graphic_1 = el2img(element);
6378 int graphic_2 = el2panelimg(element);
6380 if (game.belt_dir[i] == MV_LEFT)
6382 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6383 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6387 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6388 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6393 SCAN_PLAYFIELD(x, y)
6395 int element = Feld[x][y];
6397 for (i = 0; i < NUM_BELTS; i++)
6399 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6401 int e_belt_nr = getBeltNrFromBeltElement(element);
6404 if (e_belt_nr == belt_nr)
6406 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6408 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6415 static void ToggleBeltSwitch(int x, int y)
6417 static int belt_base_element[4] =
6419 EL_CONVEYOR_BELT_1_LEFT,
6420 EL_CONVEYOR_BELT_2_LEFT,
6421 EL_CONVEYOR_BELT_3_LEFT,
6422 EL_CONVEYOR_BELT_4_LEFT
6424 static int belt_base_active_element[4] =
6426 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6427 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6428 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6429 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6431 static int belt_base_switch_element[4] =
6433 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6434 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6435 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6436 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6438 static int belt_move_dir[4] =
6446 int element = Feld[x][y];
6447 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6448 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6449 int belt_dir = belt_move_dir[belt_dir_nr];
6452 if (!IS_BELT_SWITCH(element))
6455 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6456 game.belt_dir[belt_nr] = belt_dir;
6458 if (belt_dir_nr == 3)
6461 /* set frame order for belt animation graphic according to belt direction */
6462 for (i = 0; i < NUM_BELT_PARTS; i++)
6464 int element = belt_base_active_element[belt_nr] + i;
6465 int graphic_1 = el2img(element);
6466 int graphic_2 = el2panelimg(element);
6468 if (belt_dir == MV_LEFT)
6470 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6471 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6475 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6476 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6480 SCAN_PLAYFIELD(xx, yy)
6482 int element = Feld[xx][yy];
6484 if (IS_BELT_SWITCH(element))
6486 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6488 if (e_belt_nr == belt_nr)
6490 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6491 TEST_DrawLevelField(xx, yy);
6494 else if (IS_BELT(element) && belt_dir != MV_NONE)
6496 int e_belt_nr = getBeltNrFromBeltElement(element);
6498 if (e_belt_nr == belt_nr)
6500 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6502 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6503 TEST_DrawLevelField(xx, yy);
6506 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6508 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6510 if (e_belt_nr == belt_nr)
6512 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6514 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6515 TEST_DrawLevelField(xx, yy);
6521 static void ToggleSwitchgateSwitch(int x, int y)
6525 game.switchgate_pos = !game.switchgate_pos;
6527 SCAN_PLAYFIELD(xx, yy)
6529 int element = Feld[xx][yy];
6531 #if !USE_BOTH_SWITCHGATE_SWITCHES
6532 if (element == EL_SWITCHGATE_SWITCH_UP ||
6533 element == EL_SWITCHGATE_SWITCH_DOWN)
6535 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6536 TEST_DrawLevelField(xx, yy);
6538 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6539 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6541 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6542 TEST_DrawLevelField(xx, yy);
6545 if (element == EL_SWITCHGATE_SWITCH_UP)
6547 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6548 TEST_DrawLevelField(xx, yy);
6550 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6552 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6553 TEST_DrawLevelField(xx, yy);
6555 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6557 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6558 TEST_DrawLevelField(xx, yy);
6560 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6562 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6563 TEST_DrawLevelField(xx, yy);
6566 else if (element == EL_SWITCHGATE_OPEN ||
6567 element == EL_SWITCHGATE_OPENING)
6569 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6571 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6573 else if (element == EL_SWITCHGATE_CLOSED ||
6574 element == EL_SWITCHGATE_CLOSING)
6576 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6578 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6583 static int getInvisibleActiveFromInvisibleElement(int element)
6585 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6586 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6587 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6591 static int getInvisibleFromInvisibleActiveElement(int element)
6593 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6594 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6595 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6599 static void RedrawAllLightSwitchesAndInvisibleElements()
6603 SCAN_PLAYFIELD(x, y)
6605 int element = Feld[x][y];
6607 if (element == EL_LIGHT_SWITCH &&
6608 game.light_time_left > 0)
6610 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6611 TEST_DrawLevelField(x, y);
6613 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6614 game.light_time_left == 0)
6616 Feld[x][y] = EL_LIGHT_SWITCH;
6617 TEST_DrawLevelField(x, y);
6619 else if (element == EL_EMC_DRIPPER &&
6620 game.light_time_left > 0)
6622 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6623 TEST_DrawLevelField(x, y);
6625 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6626 game.light_time_left == 0)
6628 Feld[x][y] = EL_EMC_DRIPPER;
6629 TEST_DrawLevelField(x, y);
6631 else if (element == EL_INVISIBLE_STEELWALL ||
6632 element == EL_INVISIBLE_WALL ||
6633 element == EL_INVISIBLE_SAND)
6635 if (game.light_time_left > 0)
6636 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6638 TEST_DrawLevelField(x, y);
6640 /* uncrumble neighbour fields, if needed */
6641 if (element == EL_INVISIBLE_SAND)
6642 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6644 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6645 element == EL_INVISIBLE_WALL_ACTIVE ||
6646 element == EL_INVISIBLE_SAND_ACTIVE)
6648 if (game.light_time_left == 0)
6649 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6651 TEST_DrawLevelField(x, y);
6653 /* re-crumble neighbour fields, if needed */
6654 if (element == EL_INVISIBLE_SAND)
6655 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6660 static void RedrawAllInvisibleElementsForLenses()
6664 SCAN_PLAYFIELD(x, y)
6666 int element = Feld[x][y];
6668 if (element == EL_EMC_DRIPPER &&
6669 game.lenses_time_left > 0)
6671 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6672 TEST_DrawLevelField(x, y);
6674 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6675 game.lenses_time_left == 0)
6677 Feld[x][y] = EL_EMC_DRIPPER;
6678 TEST_DrawLevelField(x, y);
6680 else if (element == EL_INVISIBLE_STEELWALL ||
6681 element == EL_INVISIBLE_WALL ||
6682 element == EL_INVISIBLE_SAND)
6684 if (game.lenses_time_left > 0)
6685 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6687 TEST_DrawLevelField(x, y);
6689 /* uncrumble neighbour fields, if needed */
6690 if (element == EL_INVISIBLE_SAND)
6691 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6693 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6694 element == EL_INVISIBLE_WALL_ACTIVE ||
6695 element == EL_INVISIBLE_SAND_ACTIVE)
6697 if (game.lenses_time_left == 0)
6698 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6700 TEST_DrawLevelField(x, y);
6702 /* re-crumble neighbour fields, if needed */
6703 if (element == EL_INVISIBLE_SAND)
6704 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6709 static void RedrawAllInvisibleElementsForMagnifier()
6713 SCAN_PLAYFIELD(x, y)
6715 int element = Feld[x][y];
6717 if (element == EL_EMC_FAKE_GRASS &&
6718 game.magnify_time_left > 0)
6720 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6721 TEST_DrawLevelField(x, y);
6723 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6724 game.magnify_time_left == 0)
6726 Feld[x][y] = EL_EMC_FAKE_GRASS;
6727 TEST_DrawLevelField(x, y);
6729 else if (IS_GATE_GRAY(element) &&
6730 game.magnify_time_left > 0)
6732 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6733 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6734 IS_EM_GATE_GRAY(element) ?
6735 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6736 IS_EMC_GATE_GRAY(element) ?
6737 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6738 IS_DC_GATE_GRAY(element) ?
6739 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6741 TEST_DrawLevelField(x, y);
6743 else if (IS_GATE_GRAY_ACTIVE(element) &&
6744 game.magnify_time_left == 0)
6746 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6747 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6748 IS_EM_GATE_GRAY_ACTIVE(element) ?
6749 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6750 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6751 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6752 IS_DC_GATE_GRAY_ACTIVE(element) ?
6753 EL_DC_GATE_WHITE_GRAY :
6755 TEST_DrawLevelField(x, y);
6760 static void ToggleLightSwitch(int x, int y)
6762 int element = Feld[x][y];
6764 game.light_time_left =
6765 (element == EL_LIGHT_SWITCH ?
6766 level.time_light * FRAMES_PER_SECOND : 0);
6768 RedrawAllLightSwitchesAndInvisibleElements();
6771 static void ActivateTimegateSwitch(int x, int y)
6775 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6777 SCAN_PLAYFIELD(xx, yy)
6779 int element = Feld[xx][yy];
6781 if (element == EL_TIMEGATE_CLOSED ||
6782 element == EL_TIMEGATE_CLOSING)
6784 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6785 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6789 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6791 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6792 TEST_DrawLevelField(xx, yy);
6799 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6800 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6802 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6806 void Impact(int x, int y)
6808 boolean last_line = (y == lev_fieldy - 1);
6809 boolean object_hit = FALSE;
6810 boolean impact = (last_line || object_hit);
6811 int element = Feld[x][y];
6812 int smashed = EL_STEELWALL;
6814 if (!last_line) /* check if element below was hit */
6816 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6819 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6820 MovDir[x][y + 1] != MV_DOWN ||
6821 MovPos[x][y + 1] <= TILEY / 2));
6823 /* do not smash moving elements that left the smashed field in time */
6824 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6825 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6828 #if USE_QUICKSAND_IMPACT_BUGFIX
6829 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6831 RemoveMovingField(x, y + 1);
6832 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6833 Feld[x][y + 2] = EL_ROCK;
6834 TEST_DrawLevelField(x, y + 2);
6839 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6841 RemoveMovingField(x, y + 1);
6842 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6843 Feld[x][y + 2] = EL_ROCK;
6844 TEST_DrawLevelField(x, y + 2);
6851 smashed = MovingOrBlocked2Element(x, y + 1);
6853 impact = (last_line || object_hit);
6856 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6858 SplashAcid(x, y + 1);
6862 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6863 /* only reset graphic animation if graphic really changes after impact */
6865 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6867 ResetGfxAnimation(x, y);
6868 TEST_DrawLevelField(x, y);
6871 if (impact && CAN_EXPLODE_IMPACT(element))
6876 else if (impact && element == EL_PEARL &&
6877 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6879 ResetGfxAnimation(x, y);
6881 Feld[x][y] = EL_PEARL_BREAKING;
6882 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6885 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6887 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6892 if (impact && element == EL_AMOEBA_DROP)
6894 if (object_hit && IS_PLAYER(x, y + 1))
6895 KillPlayerUnlessEnemyProtected(x, y + 1);
6896 else if (object_hit && smashed == EL_PENGUIN)
6900 Feld[x][y] = EL_AMOEBA_GROWING;
6901 Store[x][y] = EL_AMOEBA_WET;
6903 ResetRandomAnimationValue(x, y);
6908 if (object_hit) /* check which object was hit */
6910 if ((CAN_PASS_MAGIC_WALL(element) &&
6911 (smashed == EL_MAGIC_WALL ||
6912 smashed == EL_BD_MAGIC_WALL)) ||
6913 (CAN_PASS_DC_MAGIC_WALL(element) &&
6914 smashed == EL_DC_MAGIC_WALL))
6917 int activated_magic_wall =
6918 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6919 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6920 EL_DC_MAGIC_WALL_ACTIVE);
6922 /* activate magic wall / mill */
6923 SCAN_PLAYFIELD(xx, yy)
6925 if (Feld[xx][yy] == smashed)
6926 Feld[xx][yy] = activated_magic_wall;
6929 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6930 game.magic_wall_active = TRUE;
6932 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6933 SND_MAGIC_WALL_ACTIVATING :
6934 smashed == EL_BD_MAGIC_WALL ?
6935 SND_BD_MAGIC_WALL_ACTIVATING :
6936 SND_DC_MAGIC_WALL_ACTIVATING));
6939 if (IS_PLAYER(x, y + 1))
6941 if (CAN_SMASH_PLAYER(element))
6943 KillPlayerUnlessEnemyProtected(x, y + 1);
6947 else if (smashed == EL_PENGUIN)
6949 if (CAN_SMASH_PLAYER(element))
6955 else if (element == EL_BD_DIAMOND)
6957 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6963 else if (((element == EL_SP_INFOTRON ||
6964 element == EL_SP_ZONK) &&
6965 (smashed == EL_SP_SNIKSNAK ||
6966 smashed == EL_SP_ELECTRON ||
6967 smashed == EL_SP_DISK_ORANGE)) ||
6968 (element == EL_SP_INFOTRON &&
6969 smashed == EL_SP_DISK_YELLOW))
6974 else if (CAN_SMASH_EVERYTHING(element))
6976 if (IS_CLASSIC_ENEMY(smashed) ||
6977 CAN_EXPLODE_SMASHED(smashed))
6982 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6984 if (smashed == EL_LAMP ||
6985 smashed == EL_LAMP_ACTIVE)
6990 else if (smashed == EL_NUT)
6992 Feld[x][y + 1] = EL_NUT_BREAKING;
6993 PlayLevelSound(x, y, SND_NUT_BREAKING);
6994 RaiseScoreElement(EL_NUT);
6997 else if (smashed == EL_PEARL)
6999 ResetGfxAnimation(x, y);
7001 Feld[x][y + 1] = EL_PEARL_BREAKING;
7002 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7005 else if (smashed == EL_DIAMOND)
7007 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7008 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7011 else if (IS_BELT_SWITCH(smashed))
7013 ToggleBeltSwitch(x, y + 1);
7015 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7016 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7017 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7018 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7020 ToggleSwitchgateSwitch(x, y + 1);
7022 else if (smashed == EL_LIGHT_SWITCH ||
7023 smashed == EL_LIGHT_SWITCH_ACTIVE)
7025 ToggleLightSwitch(x, y + 1);
7030 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7033 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7035 CheckElementChangeBySide(x, y + 1, smashed, element,
7036 CE_SWITCHED, CH_SIDE_TOP);
7037 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7043 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7048 /* play sound of magic wall / mill */
7050 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7051 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7052 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7054 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7055 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7056 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7057 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7058 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7059 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7064 /* play sound of object that hits the ground */
7065 if (last_line || object_hit)
7066 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7069 inline static void TurnRoundExt(int x, int y)
7081 { 0, 0 }, { 0, 0 }, { 0, 0 },
7086 int left, right, back;
7090 { MV_DOWN, MV_UP, MV_RIGHT },
7091 { MV_UP, MV_DOWN, MV_LEFT },
7093 { MV_LEFT, MV_RIGHT, MV_DOWN },
7097 { MV_RIGHT, MV_LEFT, MV_UP }
7100 int element = Feld[x][y];
7101 int move_pattern = element_info[element].move_pattern;
7103 int old_move_dir = MovDir[x][y];
7104 int left_dir = turn[old_move_dir].left;
7105 int right_dir = turn[old_move_dir].right;
7106 int back_dir = turn[old_move_dir].back;
7108 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7109 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7110 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7111 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7113 int left_x = x + left_dx, left_y = y + left_dy;
7114 int right_x = x + right_dx, right_y = y + right_dy;
7115 int move_x = x + move_dx, move_y = y + move_dy;
7119 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7121 TestIfBadThingTouchesOtherBadThing(x, y);
7123 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7124 MovDir[x][y] = right_dir;
7125 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7126 MovDir[x][y] = left_dir;
7128 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7130 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7133 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7135 TestIfBadThingTouchesOtherBadThing(x, y);
7137 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7138 MovDir[x][y] = left_dir;
7139 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7140 MovDir[x][y] = right_dir;
7142 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7144 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7147 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7149 TestIfBadThingTouchesOtherBadThing(x, y);
7151 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7152 MovDir[x][y] = left_dir;
7153 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7154 MovDir[x][y] = right_dir;
7156 if (MovDir[x][y] != old_move_dir)
7159 else if (element == EL_YAMYAM)
7161 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7162 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7164 if (can_turn_left && can_turn_right)
7165 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166 else if (can_turn_left)
7167 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168 else if (can_turn_right)
7169 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7171 MovDir[x][y] = back_dir;
7173 MovDelay[x][y] = 16 + 16 * RND(3);
7175 else if (element == EL_DARK_YAMYAM)
7177 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7179 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7182 if (can_turn_left && can_turn_right)
7183 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7184 else if (can_turn_left)
7185 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7186 else if (can_turn_right)
7187 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7189 MovDir[x][y] = back_dir;
7191 MovDelay[x][y] = 16 + 16 * RND(3);
7193 else if (element == EL_PACMAN)
7195 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7196 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7198 if (can_turn_left && can_turn_right)
7199 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7200 else if (can_turn_left)
7201 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7202 else if (can_turn_right)
7203 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7205 MovDir[x][y] = back_dir;
7207 MovDelay[x][y] = 6 + RND(40);
7209 else if (element == EL_PIG)
7211 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7212 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7213 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7214 boolean should_turn_left, should_turn_right, should_move_on;
7216 int rnd = RND(rnd_value);
7218 should_turn_left = (can_turn_left &&
7220 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7221 y + back_dy + left_dy)));
7222 should_turn_right = (can_turn_right &&
7224 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7225 y + back_dy + right_dy)));
7226 should_move_on = (can_move_on &&
7229 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7230 y + move_dy + left_dy) ||
7231 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7232 y + move_dy + right_dy)));
7234 if (should_turn_left || should_turn_right || should_move_on)
7236 if (should_turn_left && should_turn_right && should_move_on)
7237 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7238 rnd < 2 * rnd_value / 3 ? right_dir :
7240 else if (should_turn_left && should_turn_right)
7241 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7242 else if (should_turn_left && should_move_on)
7243 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7244 else if (should_turn_right && should_move_on)
7245 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7246 else if (should_turn_left)
7247 MovDir[x][y] = left_dir;
7248 else if (should_turn_right)
7249 MovDir[x][y] = right_dir;
7250 else if (should_move_on)
7251 MovDir[x][y] = old_move_dir;
7253 else if (can_move_on && rnd > rnd_value / 8)
7254 MovDir[x][y] = old_move_dir;
7255 else if (can_turn_left && can_turn_right)
7256 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7257 else if (can_turn_left && rnd > rnd_value / 8)
7258 MovDir[x][y] = left_dir;
7259 else if (can_turn_right && rnd > rnd_value/8)
7260 MovDir[x][y] = right_dir;
7262 MovDir[x][y] = back_dir;
7264 xx = x + move_xy[MovDir[x][y]].dx;
7265 yy = y + move_xy[MovDir[x][y]].dy;
7267 if (!IN_LEV_FIELD(xx, yy) ||
7268 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7269 MovDir[x][y] = old_move_dir;
7273 else if (element == EL_DRAGON)
7275 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7276 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7277 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7279 int rnd = RND(rnd_value);
7281 if (can_move_on && rnd > rnd_value / 8)
7282 MovDir[x][y] = old_move_dir;
7283 else if (can_turn_left && can_turn_right)
7284 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7285 else if (can_turn_left && rnd > rnd_value / 8)
7286 MovDir[x][y] = left_dir;
7287 else if (can_turn_right && rnd > rnd_value / 8)
7288 MovDir[x][y] = right_dir;
7290 MovDir[x][y] = back_dir;
7292 xx = x + move_xy[MovDir[x][y]].dx;
7293 yy = y + move_xy[MovDir[x][y]].dy;
7295 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7296 MovDir[x][y] = old_move_dir;
7300 else if (element == EL_MOLE)
7302 boolean can_move_on =
7303 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7304 IS_AMOEBOID(Feld[move_x][move_y]) ||
7305 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7308 boolean can_turn_left =
7309 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7310 IS_AMOEBOID(Feld[left_x][left_y])));
7312 boolean can_turn_right =
7313 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7314 IS_AMOEBOID(Feld[right_x][right_y])));
7316 if (can_turn_left && can_turn_right)
7317 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7318 else if (can_turn_left)
7319 MovDir[x][y] = left_dir;
7321 MovDir[x][y] = right_dir;
7324 if (MovDir[x][y] != old_move_dir)
7327 else if (element == EL_BALLOON)
7329 MovDir[x][y] = game.wind_direction;
7332 else if (element == EL_SPRING)
7334 #if USE_NEW_SPRING_BUMPER
7335 if (MovDir[x][y] & MV_HORIZONTAL)
7337 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7338 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7340 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7341 ResetGfxAnimation(move_x, move_y);
7342 TEST_DrawLevelField(move_x, move_y);
7344 MovDir[x][y] = back_dir;
7346 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7347 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7348 MovDir[x][y] = MV_NONE;
7351 if (MovDir[x][y] & MV_HORIZONTAL &&
7352 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7353 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7354 MovDir[x][y] = MV_NONE;
7359 else if (element == EL_ROBOT ||
7360 element == EL_SATELLITE ||
7361 element == EL_PENGUIN ||
7362 element == EL_EMC_ANDROID)
7364 int attr_x = -1, attr_y = -1;
7375 for (i = 0; i < MAX_PLAYERS; i++)
7377 struct PlayerInfo *player = &stored_player[i];
7378 int jx = player->jx, jy = player->jy;
7380 if (!player->active)
7384 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7392 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7393 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7394 game.engine_version < VERSION_IDENT(3,1,0,0)))
7400 if (element == EL_PENGUIN)
7403 static int xy[4][2] =
7411 for (i = 0; i < NUM_DIRECTIONS; i++)
7413 int ex = x + xy[i][0];
7414 int ey = y + xy[i][1];
7416 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7417 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7418 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7419 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7428 MovDir[x][y] = MV_NONE;
7430 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7431 else if (attr_x > x)
7432 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7434 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7435 else if (attr_y > y)
7436 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7438 if (element == EL_ROBOT)
7442 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7444 Moving2Blocked(x, y, &newx, &newy);
7446 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7447 MovDelay[x][y] = 8 + 8 * !RND(3);
7449 MovDelay[x][y] = 16;
7451 else if (element == EL_PENGUIN)
7457 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7459 boolean first_horiz = RND(2);
7460 int new_move_dir = MovDir[x][y];
7463 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7464 Moving2Blocked(x, y, &newx, &newy);
7466 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7470 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471 Moving2Blocked(x, y, &newx, &newy);
7473 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7476 MovDir[x][y] = old_move_dir;
7480 else if (element == EL_SATELLITE)
7486 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7488 boolean first_horiz = RND(2);
7489 int new_move_dir = MovDir[x][y];
7492 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493 Moving2Blocked(x, y, &newx, &newy);
7495 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7499 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500 Moving2Blocked(x, y, &newx, &newy);
7502 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7505 MovDir[x][y] = old_move_dir;
7509 else if (element == EL_EMC_ANDROID)
7511 static int check_pos[16] =
7513 -1, /* 0 => (invalid) */
7514 7, /* 1 => MV_LEFT */
7515 3, /* 2 => MV_RIGHT */
7516 -1, /* 3 => (invalid) */
7518 0, /* 5 => MV_LEFT | MV_UP */
7519 2, /* 6 => MV_RIGHT | MV_UP */
7520 -1, /* 7 => (invalid) */
7521 5, /* 8 => MV_DOWN */
7522 6, /* 9 => MV_LEFT | MV_DOWN */
7523 4, /* 10 => MV_RIGHT | MV_DOWN */
7524 -1, /* 11 => (invalid) */
7525 -1, /* 12 => (invalid) */
7526 -1, /* 13 => (invalid) */
7527 -1, /* 14 => (invalid) */
7528 -1, /* 15 => (invalid) */
7536 { -1, -1, MV_LEFT | MV_UP },
7538 { +1, -1, MV_RIGHT | MV_UP },
7539 { +1, 0, MV_RIGHT },
7540 { +1, +1, MV_RIGHT | MV_DOWN },
7542 { -1, +1, MV_LEFT | MV_DOWN },
7545 int start_pos, check_order;
7546 boolean can_clone = FALSE;
7549 /* check if there is any free field around current position */
7550 for (i = 0; i < 8; i++)
7552 int newx = x + check_xy[i].dx;
7553 int newy = y + check_xy[i].dy;
7555 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7563 if (can_clone) /* randomly find an element to clone */
7567 start_pos = check_pos[RND(8)];
7568 check_order = (RND(2) ? -1 : +1);
7570 for (i = 0; i < 8; i++)
7572 int pos_raw = start_pos + i * check_order;
7573 int pos = (pos_raw + 8) % 8;
7574 int newx = x + check_xy[pos].dx;
7575 int newy = y + check_xy[pos].dy;
7577 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7579 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7580 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7582 Store[x][y] = Feld[newx][newy];
7591 if (can_clone) /* randomly find a direction to move */
7595 start_pos = check_pos[RND(8)];
7596 check_order = (RND(2) ? -1 : +1);
7598 for (i = 0; i < 8; i++)
7600 int pos_raw = start_pos + i * check_order;
7601 int pos = (pos_raw + 8) % 8;
7602 int newx = x + check_xy[pos].dx;
7603 int newy = y + check_xy[pos].dy;
7604 int new_move_dir = check_xy[pos].dir;
7606 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7608 MovDir[x][y] = new_move_dir;
7609 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7618 if (can_clone) /* cloning and moving successful */
7621 /* cannot clone -- try to move towards player */
7623 start_pos = check_pos[MovDir[x][y] & 0x0f];
7624 check_order = (RND(2) ? -1 : +1);
7626 for (i = 0; i < 3; i++)
7628 /* first check start_pos, then previous/next or (next/previous) pos */
7629 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7630 int pos = (pos_raw + 8) % 8;
7631 int newx = x + check_xy[pos].dx;
7632 int newy = y + check_xy[pos].dy;
7633 int new_move_dir = check_xy[pos].dir;
7635 if (IS_PLAYER(newx, newy))
7638 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7640 MovDir[x][y] = new_move_dir;
7641 MovDelay[x][y] = level.android_move_time * 8 + 1;
7648 else if (move_pattern == MV_TURNING_LEFT ||
7649 move_pattern == MV_TURNING_RIGHT ||
7650 move_pattern == MV_TURNING_LEFT_RIGHT ||
7651 move_pattern == MV_TURNING_RIGHT_LEFT ||
7652 move_pattern == MV_TURNING_RANDOM ||
7653 move_pattern == MV_ALL_DIRECTIONS)
7655 boolean can_turn_left =
7656 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7657 boolean can_turn_right =
7658 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7660 if (element_info[element].move_stepsize == 0) /* "not moving" */
7663 if (move_pattern == MV_TURNING_LEFT)
7664 MovDir[x][y] = left_dir;
7665 else if (move_pattern == MV_TURNING_RIGHT)
7666 MovDir[x][y] = right_dir;
7667 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7668 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7669 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7670 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7671 else if (move_pattern == MV_TURNING_RANDOM)
7672 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7673 can_turn_right && !can_turn_left ? right_dir :
7674 RND(2) ? left_dir : right_dir);
7675 else if (can_turn_left && can_turn_right)
7676 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7677 else if (can_turn_left)
7678 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7679 else if (can_turn_right)
7680 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7682 MovDir[x][y] = back_dir;
7684 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7686 else if (move_pattern == MV_HORIZONTAL ||
7687 move_pattern == MV_VERTICAL)
7689 if (move_pattern & old_move_dir)
7690 MovDir[x][y] = back_dir;
7691 else if (move_pattern == MV_HORIZONTAL)
7692 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7693 else if (move_pattern == MV_VERTICAL)
7694 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7696 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7698 else if (move_pattern & MV_ANY_DIRECTION)
7700 MovDir[x][y] = move_pattern;
7701 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7703 else if (move_pattern & MV_WIND_DIRECTION)
7705 MovDir[x][y] = game.wind_direction;
7706 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7708 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7710 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7711 MovDir[x][y] = left_dir;
7712 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7713 MovDir[x][y] = right_dir;
7715 if (MovDir[x][y] != old_move_dir)
7716 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7718 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7720 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7721 MovDir[x][y] = right_dir;
7722 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7723 MovDir[x][y] = left_dir;
7725 if (MovDir[x][y] != old_move_dir)
7726 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7728 else if (move_pattern == MV_TOWARDS_PLAYER ||
7729 move_pattern == MV_AWAY_FROM_PLAYER)
7731 int attr_x = -1, attr_y = -1;
7733 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7744 for (i = 0; i < MAX_PLAYERS; i++)
7746 struct PlayerInfo *player = &stored_player[i];
7747 int jx = player->jx, jy = player->jy;
7749 if (!player->active)
7753 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7761 MovDir[x][y] = MV_NONE;
7763 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7764 else if (attr_x > x)
7765 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7767 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7768 else if (attr_y > y)
7769 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7771 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7773 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7775 boolean first_horiz = RND(2);
7776 int new_move_dir = MovDir[x][y];
7778 if (element_info[element].move_stepsize == 0) /* "not moving" */
7780 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7781 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7787 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7788 Moving2Blocked(x, y, &newx, &newy);
7790 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7794 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7795 Moving2Blocked(x, y, &newx, &newy);
7797 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7800 MovDir[x][y] = old_move_dir;
7803 else if (move_pattern == MV_WHEN_PUSHED ||
7804 move_pattern == MV_WHEN_DROPPED)
7806 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7807 MovDir[x][y] = MV_NONE;
7811 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7813 static int test_xy[7][2] =
7823 static int test_dir[7] =
7833 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7834 int move_preference = -1000000; /* start with very low preference */
7835 int new_move_dir = MV_NONE;
7836 int start_test = RND(4);
7839 for (i = 0; i < NUM_DIRECTIONS; i++)
7841 int move_dir = test_dir[start_test + i];
7842 int move_dir_preference;
7844 xx = x + test_xy[start_test + i][0];
7845 yy = y + test_xy[start_test + i][1];
7847 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7848 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7850 new_move_dir = move_dir;
7855 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7858 move_dir_preference = -1 * RunnerVisit[xx][yy];
7859 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7860 move_dir_preference = PlayerVisit[xx][yy];
7862 if (move_dir_preference > move_preference)
7864 /* prefer field that has not been visited for the longest time */
7865 move_preference = move_dir_preference;
7866 new_move_dir = move_dir;
7868 else if (move_dir_preference == move_preference &&
7869 move_dir == old_move_dir)
7871 /* prefer last direction when all directions are preferred equally */
7872 move_preference = move_dir_preference;
7873 new_move_dir = move_dir;
7877 MovDir[x][y] = new_move_dir;
7878 if (old_move_dir != new_move_dir)
7879 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7883 static void TurnRound(int x, int y)
7885 int direction = MovDir[x][y];
7889 GfxDir[x][y] = MovDir[x][y];
7891 if (direction != MovDir[x][y])
7895 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7897 ResetGfxFrame(x, y, FALSE);
7900 static boolean JustBeingPushed(int x, int y)
7904 for (i = 0; i < MAX_PLAYERS; i++)
7906 struct PlayerInfo *player = &stored_player[i];
7908 if (player->active && player->is_pushing && player->MovPos)
7910 int next_jx = player->jx + (player->jx - player->last_jx);
7911 int next_jy = player->jy + (player->jy - player->last_jy);
7913 if (x == next_jx && y == next_jy)
7921 void StartMoving(int x, int y)
7923 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7924 int element = Feld[x][y];
7929 if (MovDelay[x][y] == 0)
7930 GfxAction[x][y] = ACTION_DEFAULT;
7932 if (CAN_FALL(element) && y < lev_fieldy - 1)
7934 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7935 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7936 if (JustBeingPushed(x, y))
7939 if (element == EL_QUICKSAND_FULL)
7941 if (IS_FREE(x, y + 1))
7943 InitMovingField(x, y, MV_DOWN);
7944 started_moving = TRUE;
7946 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7947 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7948 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7949 Store[x][y] = EL_ROCK;
7951 Store[x][y] = EL_ROCK;
7954 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7956 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7958 if (!MovDelay[x][y])
7960 MovDelay[x][y] = TILEY + 1;
7962 ResetGfxAnimation(x, y);
7963 ResetGfxAnimation(x, y + 1);
7968 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7969 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7976 Feld[x][y] = EL_QUICKSAND_EMPTY;
7977 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7978 Store[x][y + 1] = Store[x][y];
7981 PlayLevelSoundAction(x, y, ACTION_FILLING);
7983 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7985 if (!MovDelay[x][y])
7987 MovDelay[x][y] = TILEY + 1;
7989 ResetGfxAnimation(x, y);
7990 ResetGfxAnimation(x, y + 1);
7995 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7996 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8003 Feld[x][y] = EL_QUICKSAND_EMPTY;
8004 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8005 Store[x][y + 1] = Store[x][y];
8008 PlayLevelSoundAction(x, y, ACTION_FILLING);
8011 else if (element == EL_QUICKSAND_FAST_FULL)
8013 if (IS_FREE(x, y + 1))
8015 InitMovingField(x, y, MV_DOWN);
8016 started_moving = TRUE;
8018 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8019 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8020 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8021 Store[x][y] = EL_ROCK;
8023 Store[x][y] = EL_ROCK;
8026 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8028 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8030 if (!MovDelay[x][y])
8032 MovDelay[x][y] = TILEY + 1;
8034 ResetGfxAnimation(x, y);
8035 ResetGfxAnimation(x, y + 1);
8040 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8041 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8048 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8049 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8050 Store[x][y + 1] = Store[x][y];
8053 PlayLevelSoundAction(x, y, ACTION_FILLING);
8055 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8057 if (!MovDelay[x][y])
8059 MovDelay[x][y] = TILEY + 1;
8061 ResetGfxAnimation(x, y);
8062 ResetGfxAnimation(x, y + 1);
8067 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8068 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8075 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8076 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8077 Store[x][y + 1] = Store[x][y];
8080 PlayLevelSoundAction(x, y, ACTION_FILLING);
8083 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8086 InitMovingField(x, y, MV_DOWN);
8087 started_moving = TRUE;
8089 Feld[x][y] = EL_QUICKSAND_FILLING;
8090 Store[x][y] = element;
8092 PlayLevelSoundAction(x, y, ACTION_FILLING);
8094 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8095 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8097 InitMovingField(x, y, MV_DOWN);
8098 started_moving = TRUE;
8100 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8101 Store[x][y] = element;
8103 PlayLevelSoundAction(x, y, ACTION_FILLING);
8105 else if (element == EL_MAGIC_WALL_FULL)
8107 if (IS_FREE(x, y + 1))
8109 InitMovingField(x, y, MV_DOWN);
8110 started_moving = TRUE;
8112 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8113 Store[x][y] = EL_CHANGED(Store[x][y]);
8115 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8117 if (!MovDelay[x][y])
8118 MovDelay[x][y] = TILEY/4 + 1;
8127 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8128 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8129 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8133 else if (element == EL_BD_MAGIC_WALL_FULL)
8135 if (IS_FREE(x, y + 1))
8137 InitMovingField(x, y, MV_DOWN);
8138 started_moving = TRUE;
8140 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8141 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8143 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8145 if (!MovDelay[x][y])
8146 MovDelay[x][y] = TILEY/4 + 1;
8155 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8156 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8157 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8161 else if (element == EL_DC_MAGIC_WALL_FULL)
8163 if (IS_FREE(x, y + 1))
8165 InitMovingField(x, y, MV_DOWN);
8166 started_moving = TRUE;
8168 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8169 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8171 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8173 if (!MovDelay[x][y])
8174 MovDelay[x][y] = TILEY/4 + 1;
8183 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8184 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8185 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8189 else if ((CAN_PASS_MAGIC_WALL(element) &&
8190 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8191 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8192 (CAN_PASS_DC_MAGIC_WALL(element) &&
8193 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8196 InitMovingField(x, y, MV_DOWN);
8197 started_moving = TRUE;
8200 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8201 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8202 EL_DC_MAGIC_WALL_FILLING);
8203 Store[x][y] = element;
8205 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8207 SplashAcid(x, y + 1);
8209 InitMovingField(x, y, MV_DOWN);
8210 started_moving = TRUE;
8212 Store[x][y] = EL_ACID;
8215 #if USE_FIX_IMPACT_COLLISION
8216 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8219 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8220 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8222 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8223 CAN_FALL(element) && WasJustFalling[x][y] &&
8224 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8226 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8227 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8228 (Feld[x][y + 1] == EL_BLOCKED)))
8230 /* this is needed for a special case not covered by calling "Impact()"
8231 from "ContinueMoving()": if an element moves to a tile directly below
8232 another element which was just falling on that tile (which was empty
8233 in the previous frame), the falling element above would just stop
8234 instead of smashing the element below (in previous version, the above
8235 element was just checked for "moving" instead of "falling", resulting
8236 in incorrect smashes caused by horizontal movement of the above
8237 element; also, the case of the player being the element to smash was
8238 simply not covered here... :-/ ) */
8240 CheckCollision[x][y] = 0;
8241 CheckImpact[x][y] = 0;
8245 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8247 if (MovDir[x][y] == MV_NONE)
8249 InitMovingField(x, y, MV_DOWN);
8250 started_moving = TRUE;
8253 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8255 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8256 MovDir[x][y] = MV_DOWN;
8258 InitMovingField(x, y, MV_DOWN);
8259 started_moving = TRUE;
8261 else if (element == EL_AMOEBA_DROP)
8263 Feld[x][y] = EL_AMOEBA_GROWING;
8264 Store[x][y] = EL_AMOEBA_WET;
8266 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8267 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8268 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8269 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8271 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8272 (IS_FREE(x - 1, y + 1) ||
8273 Feld[x - 1][y + 1] == EL_ACID));
8274 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8275 (IS_FREE(x + 1, y + 1) ||
8276 Feld[x + 1][y + 1] == EL_ACID));
8277 boolean can_fall_any = (can_fall_left || can_fall_right);
8278 boolean can_fall_both = (can_fall_left && can_fall_right);
8279 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8281 #if USE_NEW_ALL_SLIPPERY
8282 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8284 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8285 can_fall_right = FALSE;
8286 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8287 can_fall_left = FALSE;
8288 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8289 can_fall_right = FALSE;
8290 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291 can_fall_left = FALSE;
8293 can_fall_any = (can_fall_left || can_fall_right);
8294 can_fall_both = FALSE;
8297 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8299 if (slippery_type == SLIPPERY_ONLY_LEFT)
8300 can_fall_right = FALSE;
8301 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8302 can_fall_left = FALSE;
8303 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8304 can_fall_right = FALSE;
8305 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8306 can_fall_left = FALSE;
8308 can_fall_any = (can_fall_left || can_fall_right);
8309 can_fall_both = (can_fall_left && can_fall_right);
8313 #if USE_NEW_ALL_SLIPPERY
8315 #if USE_NEW_SP_SLIPPERY
8316 /* !!! better use the same properties as for custom elements here !!! */
8317 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8318 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8320 can_fall_right = FALSE; /* slip down on left side */
8321 can_fall_both = FALSE;
8326 #if USE_NEW_ALL_SLIPPERY
8329 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330 can_fall_right = FALSE; /* slip down on left side */
8332 can_fall_left = !(can_fall_right = RND(2));
8334 can_fall_both = FALSE;
8339 if (game.emulation == EMU_BOULDERDASH ||
8340 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8341 can_fall_right = FALSE; /* slip down on left side */
8343 can_fall_left = !(can_fall_right = RND(2));
8345 can_fall_both = FALSE;
8351 /* if not determined otherwise, prefer left side for slipping down */
8352 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8353 started_moving = TRUE;
8357 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8359 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8362 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8363 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8364 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8365 int belt_dir = game.belt_dir[belt_nr];
8367 if ((belt_dir == MV_LEFT && left_is_free) ||
8368 (belt_dir == MV_RIGHT && right_is_free))
8370 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8372 InitMovingField(x, y, belt_dir);
8373 started_moving = TRUE;
8375 Pushed[x][y] = TRUE;
8376 Pushed[nextx][y] = TRUE;
8378 GfxAction[x][y] = ACTION_DEFAULT;
8382 MovDir[x][y] = 0; /* if element was moving, stop it */
8387 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8389 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8391 if (CAN_MOVE(element) && !started_moving)
8394 int move_pattern = element_info[element].move_pattern;
8399 if (MovDir[x][y] == MV_NONE)
8401 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8402 x, y, element, element_info[element].token_name);
8403 printf("StartMoving(): This should never happen!\n");
8408 Moving2Blocked(x, y, &newx, &newy);
8410 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8413 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8414 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8416 WasJustMoving[x][y] = 0;
8417 CheckCollision[x][y] = 0;
8419 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8421 if (Feld[x][y] != element) /* element has changed */
8425 if (!MovDelay[x][y]) /* start new movement phase */
8427 /* all objects that can change their move direction after each step
8428 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8430 if (element != EL_YAMYAM &&
8431 element != EL_DARK_YAMYAM &&
8432 element != EL_PACMAN &&
8433 !(move_pattern & MV_ANY_DIRECTION) &&
8434 move_pattern != MV_TURNING_LEFT &&
8435 move_pattern != MV_TURNING_RIGHT &&
8436 move_pattern != MV_TURNING_LEFT_RIGHT &&
8437 move_pattern != MV_TURNING_RIGHT_LEFT &&
8438 move_pattern != MV_TURNING_RANDOM)
8442 if (MovDelay[x][y] && (element == EL_BUG ||
8443 element == EL_SPACESHIP ||
8444 element == EL_SP_SNIKSNAK ||
8445 element == EL_SP_ELECTRON ||
8446 element == EL_MOLE))
8447 TEST_DrawLevelField(x, y);
8451 if (MovDelay[x][y]) /* wait some time before next movement */
8455 if (element == EL_ROBOT ||
8456 element == EL_YAMYAM ||
8457 element == EL_DARK_YAMYAM)
8459 DrawLevelElementAnimationIfNeeded(x, y, element);
8460 PlayLevelSoundAction(x, y, ACTION_WAITING);
8462 else if (element == EL_SP_ELECTRON)
8463 DrawLevelElementAnimationIfNeeded(x, y, element);
8464 else if (element == EL_DRAGON)
8467 int dir = MovDir[x][y];
8468 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8469 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8470 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8471 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8472 dir == MV_UP ? IMG_FLAMES_1_UP :
8473 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8474 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8476 GfxAction[x][y] = ACTION_ATTACKING;
8478 if (IS_PLAYER(x, y))
8479 DrawPlayerField(x, y);
8481 TEST_DrawLevelField(x, y);
8483 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8485 for (i = 1; i <= 3; i++)
8487 int xx = x + i * dx;
8488 int yy = y + i * dy;
8489 int sx = SCREENX(xx);
8490 int sy = SCREENY(yy);
8491 int flame_graphic = graphic + (i - 1);
8493 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8498 int flamed = MovingOrBlocked2Element(xx, yy);
8502 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8504 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8505 RemoveMovingField(xx, yy);
8507 RemoveField(xx, yy);
8509 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8512 RemoveMovingField(xx, yy);
8515 ChangeDelay[xx][yy] = 0;
8517 Feld[xx][yy] = EL_FLAMES;
8519 if (IN_SCR_FIELD(sx, sy))
8521 TEST_DrawLevelFieldCrumbled(xx, yy);
8522 DrawGraphic(sx, sy, flame_graphic, frame);
8527 if (Feld[xx][yy] == EL_FLAMES)
8528 Feld[xx][yy] = EL_EMPTY;
8529 TEST_DrawLevelField(xx, yy);
8534 if (MovDelay[x][y]) /* element still has to wait some time */
8536 PlayLevelSoundAction(x, y, ACTION_WAITING);
8542 /* now make next step */
8544 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8546 if (DONT_COLLIDE_WITH(element) &&
8547 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8548 !PLAYER_ENEMY_PROTECTED(newx, newy))
8550 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8555 else if (CAN_MOVE_INTO_ACID(element) &&
8556 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8557 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8558 (MovDir[x][y] == MV_DOWN ||
8559 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8561 SplashAcid(newx, newy);
8562 Store[x][y] = EL_ACID;
8564 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8566 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8567 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8568 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8569 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8572 TEST_DrawLevelField(x, y);
8574 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8575 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8576 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8578 local_player->friends_still_needed--;
8579 if (!local_player->friends_still_needed &&
8580 !local_player->GameOver && AllPlayersGone)
8581 PlayerWins(local_player);
8585 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8587 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8588 TEST_DrawLevelField(newx, newy);
8590 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8592 else if (!IS_FREE(newx, newy))
8594 GfxAction[x][y] = ACTION_WAITING;
8596 if (IS_PLAYER(x, y))
8597 DrawPlayerField(x, y);
8599 TEST_DrawLevelField(x, y);
8604 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8606 if (IS_FOOD_PIG(Feld[newx][newy]))
8608 if (IS_MOVING(newx, newy))
8609 RemoveMovingField(newx, newy);
8612 Feld[newx][newy] = EL_EMPTY;
8613 TEST_DrawLevelField(newx, newy);
8616 PlayLevelSound(x, y, SND_PIG_DIGGING);
8618 else if (!IS_FREE(newx, newy))
8620 if (IS_PLAYER(x, y))
8621 DrawPlayerField(x, y);
8623 TEST_DrawLevelField(x, y);
8628 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8630 if (Store[x][y] != EL_EMPTY)
8632 boolean can_clone = FALSE;
8635 /* check if element to clone is still there */
8636 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8638 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8646 /* cannot clone or target field not free anymore -- do not clone */
8647 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8648 Store[x][y] = EL_EMPTY;
8651 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8653 if (IS_MV_DIAGONAL(MovDir[x][y]))
8655 int diagonal_move_dir = MovDir[x][y];
8656 int stored = Store[x][y];
8657 int change_delay = 8;
8660 /* android is moving diagonally */
8662 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8664 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8665 GfxElement[x][y] = EL_EMC_ANDROID;
8666 GfxAction[x][y] = ACTION_SHRINKING;
8667 GfxDir[x][y] = diagonal_move_dir;
8668 ChangeDelay[x][y] = change_delay;
8670 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8673 DrawLevelGraphicAnimation(x, y, graphic);
8674 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8676 if (Feld[newx][newy] == EL_ACID)
8678 SplashAcid(newx, newy);
8683 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8685 Store[newx][newy] = EL_EMC_ANDROID;
8686 GfxElement[newx][newy] = EL_EMC_ANDROID;
8687 GfxAction[newx][newy] = ACTION_GROWING;
8688 GfxDir[newx][newy] = diagonal_move_dir;
8689 ChangeDelay[newx][newy] = change_delay;
8691 graphic = el_act_dir2img(GfxElement[newx][newy],
8692 GfxAction[newx][newy], GfxDir[newx][newy]);
8694 DrawLevelGraphicAnimation(newx, newy, graphic);
8695 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8701 Feld[newx][newy] = EL_EMPTY;
8702 TEST_DrawLevelField(newx, newy);
8704 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8707 else if (!IS_FREE(newx, newy))
8710 if (IS_PLAYER(x, y))
8711 DrawPlayerField(x, y);
8713 TEST_DrawLevelField(x, y);
8719 else if (IS_CUSTOM_ELEMENT(element) &&
8720 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8723 if (!DigFieldByCE(newx, newy, element))
8726 int new_element = Feld[newx][newy];
8728 if (!IS_FREE(newx, newy))
8730 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8731 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8734 /* no element can dig solid indestructible elements */
8735 if (IS_INDESTRUCTIBLE(new_element) &&
8736 !IS_DIGGABLE(new_element) &&
8737 !IS_COLLECTIBLE(new_element))
8740 if (AmoebaNr[newx][newy] &&
8741 (new_element == EL_AMOEBA_FULL ||
8742 new_element == EL_BD_AMOEBA ||
8743 new_element == EL_AMOEBA_GROWING))
8745 AmoebaCnt[AmoebaNr[newx][newy]]--;
8746 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8749 if (IS_MOVING(newx, newy))
8750 RemoveMovingField(newx, newy);
8753 RemoveField(newx, newy);
8754 TEST_DrawLevelField(newx, newy);
8757 /* if digged element was about to explode, prevent the explosion */
8758 ExplodeField[newx][newy] = EX_TYPE_NONE;
8760 PlayLevelSoundAction(x, y, action);
8763 Store[newx][newy] = EL_EMPTY;
8766 /* this makes it possible to leave the removed element again */
8767 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8768 Store[newx][newy] = new_element;
8770 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8772 int move_leave_element = element_info[element].move_leave_element;
8774 /* this makes it possible to leave the removed element again */
8775 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8776 new_element : move_leave_element);
8782 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8784 RunnerVisit[x][y] = FrameCounter;
8785 PlayerVisit[x][y] /= 8; /* expire player visit path */
8788 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8790 if (!IS_FREE(newx, newy))
8792 if (IS_PLAYER(x, y))
8793 DrawPlayerField(x, y);
8795 TEST_DrawLevelField(x, y);
8801 boolean wanna_flame = !RND(10);
8802 int dx = newx - x, dy = newy - y;
8803 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8804 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8805 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8806 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8807 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8808 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8811 IS_CLASSIC_ENEMY(element1) ||
8812 IS_CLASSIC_ENEMY(element2)) &&
8813 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8814 element1 != EL_FLAMES && element2 != EL_FLAMES)
8816 ResetGfxAnimation(x, y);
8817 GfxAction[x][y] = ACTION_ATTACKING;
8819 if (IS_PLAYER(x, y))
8820 DrawPlayerField(x, y);
8822 TEST_DrawLevelField(x, y);
8824 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8826 MovDelay[x][y] = 50;
8830 RemoveField(newx, newy);
8832 Feld[newx][newy] = EL_FLAMES;
8833 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8836 RemoveField(newx1, newy1);
8838 Feld[newx1][newy1] = EL_FLAMES;
8840 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8843 RemoveField(newx2, newy2);
8845 Feld[newx2][newy2] = EL_FLAMES;
8852 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8853 Feld[newx][newy] == EL_DIAMOND)
8855 if (IS_MOVING(newx, newy))
8856 RemoveMovingField(newx, newy);
8859 Feld[newx][newy] = EL_EMPTY;
8860 TEST_DrawLevelField(newx, newy);
8863 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8865 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8866 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8868 if (AmoebaNr[newx][newy])
8870 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8871 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8872 Feld[newx][newy] == EL_BD_AMOEBA)
8873 AmoebaCnt[AmoebaNr[newx][newy]]--;
8878 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8880 RemoveMovingField(newx, newy);
8883 if (IS_MOVING(newx, newy))
8885 RemoveMovingField(newx, newy);
8890 Feld[newx][newy] = EL_EMPTY;
8891 TEST_DrawLevelField(newx, newy);
8894 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8896 else if ((element == EL_PACMAN || element == EL_MOLE)
8897 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8899 if (AmoebaNr[newx][newy])
8901 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8902 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8903 Feld[newx][newy] == EL_BD_AMOEBA)
8904 AmoebaCnt[AmoebaNr[newx][newy]]--;
8907 if (element == EL_MOLE)
8909 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8910 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8912 ResetGfxAnimation(x, y);
8913 GfxAction[x][y] = ACTION_DIGGING;
8914 TEST_DrawLevelField(x, y);
8916 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8918 return; /* wait for shrinking amoeba */
8920 else /* element == EL_PACMAN */
8922 Feld[newx][newy] = EL_EMPTY;
8923 TEST_DrawLevelField(newx, newy);
8924 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8927 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8928 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8929 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8931 /* wait for shrinking amoeba to completely disappear */
8934 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8936 /* object was running against a wall */
8941 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8942 if (move_pattern & MV_ANY_DIRECTION &&
8943 move_pattern == MovDir[x][y])
8945 int blocking_element =
8946 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8948 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8951 element = Feld[x][y]; /* element might have changed */
8955 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8956 DrawLevelElementAnimation(x, y, element);
8958 if (DONT_TOUCH(element))
8959 TestIfBadThingTouchesPlayer(x, y);
8964 InitMovingField(x, y, MovDir[x][y]);
8966 PlayLevelSoundAction(x, y, ACTION_MOVING);
8970 ContinueMoving(x, y);
8973 void ContinueMoving(int x, int y)
8975 int element = Feld[x][y];
8976 struct ElementInfo *ei = &element_info[element];
8977 int direction = MovDir[x][y];
8978 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8979 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8980 int newx = x + dx, newy = y + dy;
8981 int stored = Store[x][y];
8982 int stored_new = Store[newx][newy];
8983 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8984 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8985 boolean last_line = (newy == lev_fieldy - 1);
8987 MovPos[x][y] += getElementMoveStepsize(x, y);
8989 if (pushed_by_player) /* special case: moving object pushed by player */
8990 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8992 if (ABS(MovPos[x][y]) < TILEX)
8995 int ee = Feld[x][y];
8996 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8997 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8999 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9000 x, y, ABS(MovPos[x][y]),
9002 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9005 TEST_DrawLevelField(x, y);
9007 return; /* element is still moving */
9010 /* element reached destination field */
9012 Feld[x][y] = EL_EMPTY;
9013 Feld[newx][newy] = element;
9014 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9016 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9018 element = Feld[newx][newy] = EL_ACID;
9020 else if (element == EL_MOLE)
9022 Feld[x][y] = EL_SAND;
9024 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9026 else if (element == EL_QUICKSAND_FILLING)
9028 element = Feld[newx][newy] = get_next_element(element);
9029 Store[newx][newy] = Store[x][y];
9031 else if (element == EL_QUICKSAND_EMPTYING)
9033 Feld[x][y] = get_next_element(element);
9034 element = Feld[newx][newy] = Store[x][y];
9036 else if (element == EL_QUICKSAND_FAST_FILLING)
9038 element = Feld[newx][newy] = get_next_element(element);
9039 Store[newx][newy] = Store[x][y];
9041 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9043 Feld[x][y] = get_next_element(element);
9044 element = Feld[newx][newy] = Store[x][y];
9046 else if (element == EL_MAGIC_WALL_FILLING)
9048 element = Feld[newx][newy] = get_next_element(element);
9049 if (!game.magic_wall_active)
9050 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9051 Store[newx][newy] = Store[x][y];
9053 else if (element == EL_MAGIC_WALL_EMPTYING)
9055 Feld[x][y] = get_next_element(element);
9056 if (!game.magic_wall_active)
9057 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9058 element = Feld[newx][newy] = Store[x][y];
9060 #if USE_NEW_CUSTOM_VALUE
9061 InitField(newx, newy, FALSE);
9064 else if (element == EL_BD_MAGIC_WALL_FILLING)
9066 element = Feld[newx][newy] = get_next_element(element);
9067 if (!game.magic_wall_active)
9068 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9069 Store[newx][newy] = Store[x][y];
9071 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9073 Feld[x][y] = get_next_element(element);
9074 if (!game.magic_wall_active)
9075 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9076 element = Feld[newx][newy] = Store[x][y];
9078 #if USE_NEW_CUSTOM_VALUE
9079 InitField(newx, newy, FALSE);
9082 else if (element == EL_DC_MAGIC_WALL_FILLING)
9084 element = Feld[newx][newy] = get_next_element(element);
9085 if (!game.magic_wall_active)
9086 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9087 Store[newx][newy] = Store[x][y];
9089 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9091 Feld[x][y] = get_next_element(element);
9092 if (!game.magic_wall_active)
9093 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9094 element = Feld[newx][newy] = Store[x][y];
9096 #if USE_NEW_CUSTOM_VALUE
9097 InitField(newx, newy, FALSE);
9100 else if (element == EL_AMOEBA_DROPPING)
9102 Feld[x][y] = get_next_element(element);
9103 element = Feld[newx][newy] = Store[x][y];
9105 else if (element == EL_SOKOBAN_OBJECT)
9108 Feld[x][y] = Back[x][y];
9110 if (Back[newx][newy])
9111 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9113 Back[x][y] = Back[newx][newy] = 0;
9116 Store[x][y] = EL_EMPTY;
9121 MovDelay[newx][newy] = 0;
9123 if (CAN_CHANGE_OR_HAS_ACTION(element))
9125 /* copy element change control values to new field */
9126 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9127 ChangePage[newx][newy] = ChangePage[x][y];
9128 ChangeCount[newx][newy] = ChangeCount[x][y];
9129 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9132 #if USE_NEW_CUSTOM_VALUE
9133 CustomValue[newx][newy] = CustomValue[x][y];
9136 ChangeDelay[x][y] = 0;
9137 ChangePage[x][y] = -1;
9138 ChangeCount[x][y] = 0;
9139 ChangeEvent[x][y] = -1;
9141 #if USE_NEW_CUSTOM_VALUE
9142 CustomValue[x][y] = 0;
9145 /* copy animation control values to new field */
9146 GfxFrame[newx][newy] = GfxFrame[x][y];
9147 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9148 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9149 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9151 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9153 /* some elements can leave other elements behind after moving */
9155 if (ei->move_leave_element != EL_EMPTY &&
9156 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9157 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9159 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9160 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9161 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9164 int move_leave_element = ei->move_leave_element;
9168 /* this makes it possible to leave the removed element again */
9169 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9170 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9172 /* this makes it possible to leave the removed element again */
9173 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9174 move_leave_element = stored;
9177 /* this makes it possible to leave the removed element again */
9178 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9179 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9180 move_leave_element = stored;
9183 Feld[x][y] = move_leave_element;
9185 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9186 MovDir[x][y] = direction;
9188 InitField(x, y, FALSE);
9190 if (GFX_CRUMBLED(Feld[x][y]))
9191 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9193 if (ELEM_IS_PLAYER(move_leave_element))
9194 RelocatePlayer(x, y, move_leave_element);
9197 /* do this after checking for left-behind element */
9198 ResetGfxAnimation(x, y); /* reset animation values for old field */
9200 if (!CAN_MOVE(element) ||
9201 (CAN_FALL(element) && direction == MV_DOWN &&
9202 (element == EL_SPRING ||
9203 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9204 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9205 GfxDir[x][y] = MovDir[newx][newy] = 0;
9207 TEST_DrawLevelField(x, y);
9208 TEST_DrawLevelField(newx, newy);
9210 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9212 /* prevent pushed element from moving on in pushed direction */
9213 if (pushed_by_player && CAN_MOVE(element) &&
9214 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9215 !(element_info[element].move_pattern & direction))
9216 TurnRound(newx, newy);
9218 /* prevent elements on conveyor belt from moving on in last direction */
9219 if (pushed_by_conveyor && CAN_FALL(element) &&
9220 direction & MV_HORIZONTAL)
9221 MovDir[newx][newy] = 0;
9223 if (!pushed_by_player)
9225 int nextx = newx + dx, nexty = newy + dy;
9226 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9228 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9230 if (CAN_FALL(element) && direction == MV_DOWN)
9231 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9233 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9234 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9236 #if USE_FIX_IMPACT_COLLISION
9237 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9238 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9242 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9244 TestIfBadThingTouchesPlayer(newx, newy);
9245 TestIfBadThingTouchesFriend(newx, newy);
9247 if (!IS_CUSTOM_ELEMENT(element))
9248 TestIfBadThingTouchesOtherBadThing(newx, newy);
9250 else if (element == EL_PENGUIN)
9251 TestIfFriendTouchesBadThing(newx, newy);
9253 if (DONT_GET_HIT_BY(element))
9255 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9258 /* give the player one last chance (one more frame) to move away */
9259 if (CAN_FALL(element) && direction == MV_DOWN &&
9260 (last_line || (!IS_FREE(x, newy + 1) &&
9261 (!IS_PLAYER(x, newy + 1) ||
9262 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9265 if (pushed_by_player && !game.use_change_when_pushing_bug)
9267 int push_side = MV_DIR_OPPOSITE(direction);
9268 struct PlayerInfo *player = PLAYERINFO(x, y);
9270 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9271 player->index_bit, push_side);
9272 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9273 player->index_bit, push_side);
9276 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9277 MovDelay[newx][newy] = 1;
9279 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9281 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9284 if (ChangePage[newx][newy] != -1) /* delayed change */
9286 int page = ChangePage[newx][newy];
9287 struct ElementChangeInfo *change = &ei->change_page[page];
9289 ChangePage[newx][newy] = -1;
9291 if (change->can_change)
9293 if (ChangeElement(newx, newy, element, page))
9295 if (change->post_change_function)
9296 change->post_change_function(newx, newy);
9300 if (change->has_action)
9301 ExecuteCustomElementAction(newx, newy, element, page);
9305 TestIfElementHitsCustomElement(newx, newy, direction);
9306 TestIfPlayerTouchesCustomElement(newx, newy);
9307 TestIfElementTouchesCustomElement(newx, newy);
9309 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9310 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9311 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9312 MV_DIR_OPPOSITE(direction));
9315 int AmoebeNachbarNr(int ax, int ay)
9318 int element = Feld[ax][ay];
9320 static int xy[4][2] =
9328 for (i = 0; i < NUM_DIRECTIONS; i++)
9330 int x = ax + xy[i][0];
9331 int y = ay + xy[i][1];
9333 if (!IN_LEV_FIELD(x, y))
9336 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9337 group_nr = AmoebaNr[x][y];
9343 void AmoebenVereinigen(int ax, int ay)
9345 int i, x, y, xx, yy;
9346 int new_group_nr = AmoebaNr[ax][ay];
9347 static int xy[4][2] =
9355 if (new_group_nr == 0)
9358 for (i = 0; i < NUM_DIRECTIONS; i++)
9363 if (!IN_LEV_FIELD(x, y))
9366 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9367 Feld[x][y] == EL_BD_AMOEBA ||
9368 Feld[x][y] == EL_AMOEBA_DEAD) &&
9369 AmoebaNr[x][y] != new_group_nr)
9371 int old_group_nr = AmoebaNr[x][y];
9373 if (old_group_nr == 0)
9376 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9377 AmoebaCnt[old_group_nr] = 0;
9378 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9379 AmoebaCnt2[old_group_nr] = 0;
9381 SCAN_PLAYFIELD(xx, yy)
9383 if (AmoebaNr[xx][yy] == old_group_nr)
9384 AmoebaNr[xx][yy] = new_group_nr;
9390 void AmoebeUmwandeln(int ax, int ay)
9394 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9396 int group_nr = AmoebaNr[ax][ay];
9401 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9402 printf("AmoebeUmwandeln(): This should never happen!\n");
9407 SCAN_PLAYFIELD(x, y)
9409 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9412 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9416 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9417 SND_AMOEBA_TURNING_TO_GEM :
9418 SND_AMOEBA_TURNING_TO_ROCK));
9423 static int xy[4][2] =
9431 for (i = 0; i < NUM_DIRECTIONS; i++)
9436 if (!IN_LEV_FIELD(x, y))
9439 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9441 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9442 SND_AMOEBA_TURNING_TO_GEM :
9443 SND_AMOEBA_TURNING_TO_ROCK));
9450 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9453 int group_nr = AmoebaNr[ax][ay];
9454 boolean done = FALSE;
9459 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9460 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9465 SCAN_PLAYFIELD(x, y)
9467 if (AmoebaNr[x][y] == group_nr &&
9468 (Feld[x][y] == EL_AMOEBA_DEAD ||
9469 Feld[x][y] == EL_BD_AMOEBA ||
9470 Feld[x][y] == EL_AMOEBA_GROWING))
9473 Feld[x][y] = new_element;
9474 InitField(x, y, FALSE);
9475 TEST_DrawLevelField(x, y);
9481 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9482 SND_BD_AMOEBA_TURNING_TO_ROCK :
9483 SND_BD_AMOEBA_TURNING_TO_GEM));
9486 void AmoebeWaechst(int x, int y)
9488 static unsigned long sound_delay = 0;
9489 static unsigned long sound_delay_value = 0;
9491 if (!MovDelay[x][y]) /* start new growing cycle */
9495 if (DelayReached(&sound_delay, sound_delay_value))
9497 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9498 sound_delay_value = 30;
9502 if (MovDelay[x][y]) /* wait some time before growing bigger */
9505 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9507 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9508 6 - MovDelay[x][y]);
9510 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9513 if (!MovDelay[x][y])
9515 Feld[x][y] = Store[x][y];
9517 TEST_DrawLevelField(x, y);
9522 void AmoebaDisappearing(int x, int y)
9524 static unsigned long sound_delay = 0;
9525 static unsigned long sound_delay_value = 0;
9527 if (!MovDelay[x][y]) /* start new shrinking cycle */
9531 if (DelayReached(&sound_delay, sound_delay_value))
9532 sound_delay_value = 30;
9535 if (MovDelay[x][y]) /* wait some time before shrinking */
9538 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9540 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9541 6 - MovDelay[x][y]);
9543 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9546 if (!MovDelay[x][y])
9548 Feld[x][y] = EL_EMPTY;
9549 TEST_DrawLevelField(x, y);
9551 /* don't let mole enter this field in this cycle;
9552 (give priority to objects falling to this field from above) */
9558 void AmoebeAbleger(int ax, int ay)
9561 int element = Feld[ax][ay];
9562 int graphic = el2img(element);
9563 int newax = ax, neway = ay;
9564 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9565 static int xy[4][2] =
9573 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9575 Feld[ax][ay] = EL_AMOEBA_DEAD;
9576 TEST_DrawLevelField(ax, ay);
9580 if (IS_ANIMATED(graphic))
9581 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9584 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9586 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9589 if (MovDelay[ax][ay])
9593 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9596 int x = ax + xy[start][0];
9597 int y = ay + xy[start][1];
9599 if (!IN_LEV_FIELD(x, y))
9602 if (IS_FREE(x, y) ||
9603 CAN_GROW_INTO(Feld[x][y]) ||
9604 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9605 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9611 if (newax == ax && neway == ay)
9614 else /* normal or "filled" (BD style) amoeba */
9617 boolean waiting_for_player = FALSE;
9619 for (i = 0; i < NUM_DIRECTIONS; i++)
9621 int j = (start + i) % 4;
9622 int x = ax + xy[j][0];
9623 int y = ay + xy[j][1];
9625 if (!IN_LEV_FIELD(x, y))
9628 if (IS_FREE(x, y) ||
9629 CAN_GROW_INTO(Feld[x][y]) ||
9630 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9631 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9637 else if (IS_PLAYER(x, y))
9638 waiting_for_player = TRUE;
9641 if (newax == ax && neway == ay) /* amoeba cannot grow */
9643 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9645 Feld[ax][ay] = EL_AMOEBA_DEAD;
9646 TEST_DrawLevelField(ax, ay);
9647 AmoebaCnt[AmoebaNr[ax][ay]]--;
9649 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9651 if (element == EL_AMOEBA_FULL)
9652 AmoebeUmwandeln(ax, ay);
9653 else if (element == EL_BD_AMOEBA)
9654 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9659 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9661 /* amoeba gets larger by growing in some direction */
9663 int new_group_nr = AmoebaNr[ax][ay];
9666 if (new_group_nr == 0)
9668 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9669 printf("AmoebeAbleger(): This should never happen!\n");
9674 AmoebaNr[newax][neway] = new_group_nr;
9675 AmoebaCnt[new_group_nr]++;
9676 AmoebaCnt2[new_group_nr]++;
9678 /* if amoeba touches other amoeba(s) after growing, unify them */
9679 AmoebenVereinigen(newax, neway);
9681 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9683 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9689 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9690 (neway == lev_fieldy - 1 && newax != ax))
9692 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9693 Store[newax][neway] = element;
9695 else if (neway == ay || element == EL_EMC_DRIPPER)
9697 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9699 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9703 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9704 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9705 Store[ax][ay] = EL_AMOEBA_DROP;
9706 ContinueMoving(ax, ay);
9710 TEST_DrawLevelField(newax, neway);
9713 void Life(int ax, int ay)
9717 int element = Feld[ax][ay];
9718 int graphic = el2img(element);
9719 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9721 boolean changed = FALSE;
9723 if (IS_ANIMATED(graphic))
9724 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9729 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9730 MovDelay[ax][ay] = life_time;
9732 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9735 if (MovDelay[ax][ay])
9739 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9741 int xx = ax+x1, yy = ay+y1;
9744 if (!IN_LEV_FIELD(xx, yy))
9747 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9749 int x = xx+x2, y = yy+y2;
9751 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9754 if (((Feld[x][y] == element ||
9755 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9757 (IS_FREE(x, y) && Stop[x][y]))
9761 if (xx == ax && yy == ay) /* field in the middle */
9763 if (nachbarn < life_parameter[0] ||
9764 nachbarn > life_parameter[1])
9766 Feld[xx][yy] = EL_EMPTY;
9768 TEST_DrawLevelField(xx, yy);
9769 Stop[xx][yy] = TRUE;
9773 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9774 { /* free border field */
9775 if (nachbarn >= life_parameter[2] &&
9776 nachbarn <= life_parameter[3])
9778 Feld[xx][yy] = element;
9779 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9781 TEST_DrawLevelField(xx, yy);
9782 Stop[xx][yy] = TRUE;
9789 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9790 SND_GAME_OF_LIFE_GROWING);
9793 static void InitRobotWheel(int x, int y)
9795 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9798 static void RunRobotWheel(int x, int y)
9800 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9803 static void StopRobotWheel(int x, int y)
9805 if (ZX == x && ZY == y)
9809 game.robot_wheel_active = FALSE;
9813 static void InitTimegateWheel(int x, int y)
9815 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9818 static void RunTimegateWheel(int x, int y)
9820 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9823 static void InitMagicBallDelay(int x, int y)
9826 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9828 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9832 static void ActivateMagicBall(int bx, int by)
9836 if (level.ball_random)
9838 int pos_border = RND(8); /* select one of the eight border elements */
9839 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9840 int xx = pos_content % 3;
9841 int yy = pos_content / 3;
9846 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9847 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9851 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9853 int xx = x - bx + 1;
9854 int yy = y - by + 1;
9856 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9857 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9861 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9864 void CheckExit(int x, int y)
9866 if (local_player->gems_still_needed > 0 ||
9867 local_player->sokobanfields_still_needed > 0 ||
9868 local_player->lights_still_needed > 0)
9870 int element = Feld[x][y];
9871 int graphic = el2img(element);
9873 if (IS_ANIMATED(graphic))
9874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9879 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9882 Feld[x][y] = EL_EXIT_OPENING;
9884 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9887 void CheckExitEM(int x, int y)
9889 if (local_player->gems_still_needed > 0 ||
9890 local_player->sokobanfields_still_needed > 0 ||
9891 local_player->lights_still_needed > 0)
9893 int element = Feld[x][y];
9894 int graphic = el2img(element);
9896 if (IS_ANIMATED(graphic))
9897 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9902 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9905 Feld[x][y] = EL_EM_EXIT_OPENING;
9907 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9910 void CheckExitSteel(int x, int y)
9912 if (local_player->gems_still_needed > 0 ||
9913 local_player->sokobanfields_still_needed > 0 ||
9914 local_player->lights_still_needed > 0)
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9928 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9930 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9933 void CheckExitSteelEM(int x, int y)
9935 if (local_player->gems_still_needed > 0 ||
9936 local_player->sokobanfields_still_needed > 0 ||
9937 local_player->lights_still_needed > 0)
9939 int element = Feld[x][y];
9940 int graphic = el2img(element);
9942 if (IS_ANIMATED(graphic))
9943 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9948 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9951 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9953 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9956 void CheckExitSP(int x, int y)
9958 if (local_player->gems_still_needed > 0)
9960 int element = Feld[x][y];
9961 int graphic = el2img(element);
9963 if (IS_ANIMATED(graphic))
9964 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9969 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9972 Feld[x][y] = EL_SP_EXIT_OPENING;
9974 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9977 static void CloseAllOpenTimegates()
9981 SCAN_PLAYFIELD(x, y)
9983 int element = Feld[x][y];
9985 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9987 Feld[x][y] = EL_TIMEGATE_CLOSING;
9989 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9994 void DrawTwinkleOnField(int x, int y)
9996 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9999 if (Feld[x][y] == EL_BD_DIAMOND)
10002 if (MovDelay[x][y] == 0) /* next animation frame */
10003 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10005 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10009 DrawLevelElementAnimation(x, y, Feld[x][y]);
10011 if (MovDelay[x][y] != 0)
10013 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10014 10 - MovDelay[x][y]);
10016 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10021 void MauerWaechst(int x, int y)
10025 if (!MovDelay[x][y]) /* next animation frame */
10026 MovDelay[x][y] = 3 * delay;
10028 if (MovDelay[x][y]) /* wait some time before next frame */
10032 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10034 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10035 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10037 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10040 if (!MovDelay[x][y])
10042 if (MovDir[x][y] == MV_LEFT)
10044 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10045 TEST_DrawLevelField(x - 1, y);
10047 else if (MovDir[x][y] == MV_RIGHT)
10049 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10050 TEST_DrawLevelField(x + 1, y);
10052 else if (MovDir[x][y] == MV_UP)
10054 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10055 TEST_DrawLevelField(x, y - 1);
10059 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10060 TEST_DrawLevelField(x, y + 1);
10063 Feld[x][y] = Store[x][y];
10065 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10066 TEST_DrawLevelField(x, y);
10071 void MauerAbleger(int ax, int ay)
10073 int element = Feld[ax][ay];
10074 int graphic = el2img(element);
10075 boolean oben_frei = FALSE, unten_frei = FALSE;
10076 boolean links_frei = FALSE, rechts_frei = FALSE;
10077 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10078 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10079 boolean new_wall = FALSE;
10081 if (IS_ANIMATED(graphic))
10082 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10084 if (!MovDelay[ax][ay]) /* start building new wall */
10085 MovDelay[ax][ay] = 6;
10087 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10089 MovDelay[ax][ay]--;
10090 if (MovDelay[ax][ay])
10094 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10096 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10098 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10100 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10101 rechts_frei = TRUE;
10103 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10104 element == EL_EXPANDABLE_WALL_ANY)
10108 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10109 Store[ax][ay-1] = element;
10110 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10111 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10112 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10113 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10118 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10119 Store[ax][ay+1] = element;
10120 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10121 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10122 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10123 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10128 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10129 element == EL_EXPANDABLE_WALL_ANY ||
10130 element == EL_EXPANDABLE_WALL ||
10131 element == EL_BD_EXPANDABLE_WALL)
10135 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136 Store[ax-1][ay] = element;
10137 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10138 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10139 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10140 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10146 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10147 Store[ax+1][ay] = element;
10148 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10149 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10150 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10151 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10156 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10157 TEST_DrawLevelField(ax, ay);
10159 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10160 oben_massiv = TRUE;
10161 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10162 unten_massiv = TRUE;
10163 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10164 links_massiv = TRUE;
10165 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10166 rechts_massiv = TRUE;
10168 if (((oben_massiv && unten_massiv) ||
10169 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10170 element == EL_EXPANDABLE_WALL) &&
10171 ((links_massiv && rechts_massiv) ||
10172 element == EL_EXPANDABLE_WALL_VERTICAL))
10173 Feld[ax][ay] = EL_WALL;
10176 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10179 void MauerAblegerStahl(int ax, int ay)
10181 int element = Feld[ax][ay];
10182 int graphic = el2img(element);
10183 boolean oben_frei = FALSE, unten_frei = FALSE;
10184 boolean links_frei = FALSE, rechts_frei = FALSE;
10185 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10186 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10187 boolean new_wall = FALSE;
10189 if (IS_ANIMATED(graphic))
10190 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10192 if (!MovDelay[ax][ay]) /* start building new wall */
10193 MovDelay[ax][ay] = 6;
10195 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10197 MovDelay[ax][ay]--;
10198 if (MovDelay[ax][ay])
10202 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10204 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10206 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10208 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10209 rechts_frei = TRUE;
10211 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10212 element == EL_EXPANDABLE_STEELWALL_ANY)
10216 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10217 Store[ax][ay-1] = element;
10218 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10219 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10220 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10221 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10226 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10227 Store[ax][ay+1] = element;
10228 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10229 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10230 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10231 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10236 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10237 element == EL_EXPANDABLE_STEELWALL_ANY)
10241 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242 Store[ax-1][ay] = element;
10243 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10244 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10245 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10246 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10252 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10253 Store[ax+1][ay] = element;
10254 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10255 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10256 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10257 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10262 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10263 oben_massiv = TRUE;
10264 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10265 unten_massiv = TRUE;
10266 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10267 links_massiv = TRUE;
10268 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10269 rechts_massiv = TRUE;
10271 if (((oben_massiv && unten_massiv) ||
10272 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10273 ((links_massiv && rechts_massiv) ||
10274 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10275 Feld[ax][ay] = EL_STEELWALL;
10278 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10281 void CheckForDragon(int x, int y)
10284 boolean dragon_found = FALSE;
10285 static int xy[4][2] =
10293 for (i = 0; i < NUM_DIRECTIONS; i++)
10295 for (j = 0; j < 4; j++)
10297 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10299 if (IN_LEV_FIELD(xx, yy) &&
10300 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10302 if (Feld[xx][yy] == EL_DRAGON)
10303 dragon_found = TRUE;
10312 for (i = 0; i < NUM_DIRECTIONS; i++)
10314 for (j = 0; j < 3; j++)
10316 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10318 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10320 Feld[xx][yy] = EL_EMPTY;
10321 TEST_DrawLevelField(xx, yy);
10330 static void InitBuggyBase(int x, int y)
10332 int element = Feld[x][y];
10333 int activating_delay = FRAMES_PER_SECOND / 4;
10335 ChangeDelay[x][y] =
10336 (element == EL_SP_BUGGY_BASE ?
10337 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10338 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10340 element == EL_SP_BUGGY_BASE_ACTIVE ?
10341 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10344 static void WarnBuggyBase(int x, int y)
10347 static int xy[4][2] =
10355 for (i = 0; i < NUM_DIRECTIONS; i++)
10357 int xx = x + xy[i][0];
10358 int yy = y + xy[i][1];
10360 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10362 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10369 static void InitTrap(int x, int y)
10371 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10374 static void ActivateTrap(int x, int y)
10376 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10379 static void ChangeActiveTrap(int x, int y)
10381 int graphic = IMG_TRAP_ACTIVE;
10383 /* if new animation frame was drawn, correct crumbled sand border */
10384 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10385 TEST_DrawLevelFieldCrumbled(x, y);
10388 static int getSpecialActionElement(int element, int number, int base_element)
10390 return (element != EL_EMPTY ? element :
10391 number != -1 ? base_element + number - 1 :
10395 static int getModifiedActionNumber(int value_old, int operator, int operand,
10396 int value_min, int value_max)
10398 int value_new = (operator == CA_MODE_SET ? operand :
10399 operator == CA_MODE_ADD ? value_old + operand :
10400 operator == CA_MODE_SUBTRACT ? value_old - operand :
10401 operator == CA_MODE_MULTIPLY ? value_old * operand :
10402 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10403 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10406 return (value_new < value_min ? value_min :
10407 value_new > value_max ? value_max :
10411 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10413 struct ElementInfo *ei = &element_info[element];
10414 struct ElementChangeInfo *change = &ei->change_page[page];
10415 int target_element = change->target_element;
10416 int action_type = change->action_type;
10417 int action_mode = change->action_mode;
10418 int action_arg = change->action_arg;
10419 int action_element = change->action_element;
10422 if (!change->has_action)
10425 /* ---------- determine action paramater values -------------------------- */
10427 int level_time_value =
10428 (level.time > 0 ? TimeLeft :
10431 int action_arg_element_raw =
10432 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10433 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10434 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10435 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10436 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10437 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10438 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10440 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10443 if (action_arg_element_raw == EL_GROUP_START)
10444 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10447 int action_arg_direction =
10448 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10449 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10450 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10451 change->actual_trigger_side :
10452 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10453 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10456 int action_arg_number_min =
10457 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10460 int action_arg_number_max =
10461 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10462 action_type == CA_SET_LEVEL_GEMS ? 999 :
10463 action_type == CA_SET_LEVEL_TIME ? 9999 :
10464 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10465 action_type == CA_SET_CE_VALUE ? 9999 :
10466 action_type == CA_SET_CE_SCORE ? 9999 :
10469 int action_arg_number_reset =
10470 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10471 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10472 action_type == CA_SET_LEVEL_TIME ? level.time :
10473 action_type == CA_SET_LEVEL_SCORE ? 0 :
10474 #if USE_NEW_CUSTOM_VALUE
10475 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10477 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10479 action_type == CA_SET_CE_SCORE ? 0 :
10482 int action_arg_number =
10483 (action_arg <= CA_ARG_MAX ? action_arg :
10484 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10485 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10486 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10487 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10488 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10489 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10490 #if USE_NEW_CUSTOM_VALUE
10491 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10493 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10495 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10496 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10497 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10498 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10499 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10500 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10501 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10502 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10503 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10504 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10505 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10506 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10507 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10508 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10511 int action_arg_number_old =
10512 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10513 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10514 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10515 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10516 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10519 int action_arg_number_new =
10520 getModifiedActionNumber(action_arg_number_old,
10521 action_mode, action_arg_number,
10522 action_arg_number_min, action_arg_number_max);
10525 int trigger_player_bits =
10526 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10527 change->actual_trigger_player_bits : change->trigger_player);
10529 int trigger_player_bits =
10530 (change->actual_trigger_player >= EL_PLAYER_1 &&
10531 change->actual_trigger_player <= EL_PLAYER_4 ?
10532 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10536 int action_arg_player_bits =
10537 (action_arg >= CA_ARG_PLAYER_1 &&
10538 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10539 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10540 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10543 /* ---------- execute action -------------------------------------------- */
10545 switch (action_type)
10552 /* ---------- level actions ------------------------------------------- */
10554 case CA_RESTART_LEVEL:
10556 game.restart_level = TRUE;
10561 case CA_SHOW_ENVELOPE:
10563 int element = getSpecialActionElement(action_arg_element,
10564 action_arg_number, EL_ENVELOPE_1);
10566 if (IS_ENVELOPE(element))
10567 local_player->show_envelope = element;
10572 case CA_SET_LEVEL_TIME:
10574 if (level.time > 0) /* only modify limited time value */
10576 TimeLeft = action_arg_number_new;
10579 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10581 DisplayGameControlValues();
10583 DrawGameValue_Time(TimeLeft);
10586 if (!TimeLeft && setup.time_limit)
10587 for (i = 0; i < MAX_PLAYERS; i++)
10588 KillPlayer(&stored_player[i]);
10594 case CA_SET_LEVEL_SCORE:
10596 local_player->score = action_arg_number_new;
10599 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10601 DisplayGameControlValues();
10603 DrawGameValue_Score(local_player->score);
10609 case CA_SET_LEVEL_GEMS:
10611 local_player->gems_still_needed = action_arg_number_new;
10614 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10616 DisplayGameControlValues();
10618 DrawGameValue_Emeralds(local_player->gems_still_needed);
10624 #if !USE_PLAYER_GRAVITY
10625 case CA_SET_LEVEL_GRAVITY:
10627 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10628 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10629 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10635 case CA_SET_LEVEL_WIND:
10637 game.wind_direction = action_arg_direction;
10642 case CA_SET_LEVEL_RANDOM_SEED:
10645 /* ensure that setting a new random seed while playing is predictable */
10646 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10648 InitRND(action_arg_number_new);
10652 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10660 for (i = 0; i < 9; i++)
10661 printf("%d, ", RND(2));
10669 /* ---------- player actions ------------------------------------------ */
10671 case CA_MOVE_PLAYER:
10673 /* automatically move to the next field in specified direction */
10674 for (i = 0; i < MAX_PLAYERS; i++)
10675 if (trigger_player_bits & (1 << i))
10676 stored_player[i].programmed_action = action_arg_direction;
10681 case CA_EXIT_PLAYER:
10683 for (i = 0; i < MAX_PLAYERS; i++)
10684 if (action_arg_player_bits & (1 << i))
10685 PlayerWins(&stored_player[i]);
10690 case CA_KILL_PLAYER:
10692 for (i = 0; i < MAX_PLAYERS; i++)
10693 if (action_arg_player_bits & (1 << i))
10694 KillPlayer(&stored_player[i]);
10699 case CA_SET_PLAYER_KEYS:
10701 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10702 int element = getSpecialActionElement(action_arg_element,
10703 action_arg_number, EL_KEY_1);
10705 if (IS_KEY(element))
10707 for (i = 0; i < MAX_PLAYERS; i++)
10709 if (trigger_player_bits & (1 << i))
10711 stored_player[i].key[KEY_NR(element)] = key_state;
10713 DrawGameDoorValues();
10721 case CA_SET_PLAYER_SPEED:
10724 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10727 for (i = 0; i < MAX_PLAYERS; i++)
10729 if (trigger_player_bits & (1 << i))
10731 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10733 if (action_arg == CA_ARG_SPEED_FASTER &&
10734 stored_player[i].cannot_move)
10736 action_arg_number = STEPSIZE_VERY_SLOW;
10738 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10739 action_arg == CA_ARG_SPEED_FASTER)
10741 action_arg_number = 2;
10742 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10745 else if (action_arg == CA_ARG_NUMBER_RESET)
10747 action_arg_number = level.initial_player_stepsize[i];
10751 getModifiedActionNumber(move_stepsize,
10754 action_arg_number_min,
10755 action_arg_number_max);
10757 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10764 case CA_SET_PLAYER_SHIELD:
10766 for (i = 0; i < MAX_PLAYERS; i++)
10768 if (trigger_player_bits & (1 << i))
10770 if (action_arg == CA_ARG_SHIELD_OFF)
10772 stored_player[i].shield_normal_time_left = 0;
10773 stored_player[i].shield_deadly_time_left = 0;
10775 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10777 stored_player[i].shield_normal_time_left = 999999;
10779 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10781 stored_player[i].shield_normal_time_left = 999999;
10782 stored_player[i].shield_deadly_time_left = 999999;
10790 #if USE_PLAYER_GRAVITY
10791 case CA_SET_PLAYER_GRAVITY:
10793 for (i = 0; i < MAX_PLAYERS; i++)
10795 if (trigger_player_bits & (1 << i))
10797 stored_player[i].gravity =
10798 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10799 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10800 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10801 stored_player[i].gravity);
10809 case CA_SET_PLAYER_ARTWORK:
10811 for (i = 0; i < MAX_PLAYERS; i++)
10813 if (trigger_player_bits & (1 << i))
10815 int artwork_element = action_arg_element;
10817 if (action_arg == CA_ARG_ELEMENT_RESET)
10819 (level.use_artwork_element[i] ? level.artwork_element[i] :
10820 stored_player[i].element_nr);
10822 #if USE_GFX_RESET_PLAYER_ARTWORK
10823 if (stored_player[i].artwork_element != artwork_element)
10824 stored_player[i].Frame = 0;
10827 stored_player[i].artwork_element = artwork_element;
10829 SetPlayerWaiting(&stored_player[i], FALSE);
10831 /* set number of special actions for bored and sleeping animation */
10832 stored_player[i].num_special_action_bored =
10833 get_num_special_action(artwork_element,
10834 ACTION_BORING_1, ACTION_BORING_LAST);
10835 stored_player[i].num_special_action_sleeping =
10836 get_num_special_action(artwork_element,
10837 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10844 case CA_SET_PLAYER_INVENTORY:
10846 for (i = 0; i < MAX_PLAYERS; i++)
10848 struct PlayerInfo *player = &stored_player[i];
10851 if (trigger_player_bits & (1 << i))
10853 int inventory_element = action_arg_element;
10855 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10856 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10857 action_arg == CA_ARG_ELEMENT_ACTION)
10859 int element = inventory_element;
10860 int collect_count = element_info[element].collect_count_initial;
10862 if (!IS_CUSTOM_ELEMENT(element))
10865 if (collect_count == 0)
10866 player->inventory_infinite_element = element;
10868 for (k = 0; k < collect_count; k++)
10869 if (player->inventory_size < MAX_INVENTORY_SIZE)
10870 player->inventory_element[player->inventory_size++] =
10873 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10874 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10875 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10877 if (player->inventory_infinite_element != EL_UNDEFINED &&
10878 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10879 action_arg_element_raw))
10880 player->inventory_infinite_element = EL_UNDEFINED;
10882 for (k = 0, j = 0; j < player->inventory_size; j++)
10884 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10885 action_arg_element_raw))
10886 player->inventory_element[k++] = player->inventory_element[j];
10889 player->inventory_size = k;
10891 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10893 if (player->inventory_size > 0)
10895 for (j = 0; j < player->inventory_size - 1; j++)
10896 player->inventory_element[j] = player->inventory_element[j + 1];
10898 player->inventory_size--;
10901 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10903 if (player->inventory_size > 0)
10904 player->inventory_size--;
10906 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10908 player->inventory_infinite_element = EL_UNDEFINED;
10909 player->inventory_size = 0;
10911 else if (action_arg == CA_ARG_INVENTORY_RESET)
10913 player->inventory_infinite_element = EL_UNDEFINED;
10914 player->inventory_size = 0;
10916 if (level.use_initial_inventory[i])
10918 for (j = 0; j < level.initial_inventory_size[i]; j++)
10920 int element = level.initial_inventory_content[i][j];
10921 int collect_count = element_info[element].collect_count_initial;
10923 if (!IS_CUSTOM_ELEMENT(element))
10926 if (collect_count == 0)
10927 player->inventory_infinite_element = element;
10929 for (k = 0; k < collect_count; k++)
10930 if (player->inventory_size < MAX_INVENTORY_SIZE)
10931 player->inventory_element[player->inventory_size++] =
10942 /* ---------- CE actions ---------------------------------------------- */
10944 case CA_SET_CE_VALUE:
10946 #if USE_NEW_CUSTOM_VALUE
10947 int last_ce_value = CustomValue[x][y];
10949 CustomValue[x][y] = action_arg_number_new;
10951 if (CustomValue[x][y] != last_ce_value)
10953 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10954 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10956 if (CustomValue[x][y] == 0)
10958 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10959 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10967 case CA_SET_CE_SCORE:
10969 #if USE_NEW_CUSTOM_VALUE
10970 int last_ce_score = ei->collect_score;
10972 ei->collect_score = action_arg_number_new;
10974 if (ei->collect_score != last_ce_score)
10976 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10977 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10979 if (ei->collect_score == 0)
10983 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10984 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10987 This is a very special case that seems to be a mixture between
10988 CheckElementChange() and CheckTriggeredElementChange(): while
10989 the first one only affects single elements that are triggered
10990 directly, the second one affects multiple elements in the playfield
10991 that are triggered indirectly by another element. This is a third
10992 case: Changing the CE score always affects multiple identical CEs,
10993 so every affected CE must be checked, not only the single CE for
10994 which the CE score was changed in the first place (as every instance
10995 of that CE shares the same CE score, and therefore also can change)!
10997 SCAN_PLAYFIELD(xx, yy)
10999 if (Feld[xx][yy] == element)
11000 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11001 CE_SCORE_GETS_ZERO);
11010 case CA_SET_CE_ARTWORK:
11012 int artwork_element = action_arg_element;
11013 boolean reset_frame = FALSE;
11016 if (action_arg == CA_ARG_ELEMENT_RESET)
11017 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11020 if (ei->gfx_element != artwork_element)
11021 reset_frame = TRUE;
11023 ei->gfx_element = artwork_element;
11025 SCAN_PLAYFIELD(xx, yy)
11027 if (Feld[xx][yy] == element)
11031 ResetGfxAnimation(xx, yy);
11032 ResetRandomAnimationValue(xx, yy);
11035 TEST_DrawLevelField(xx, yy);
11042 /* ---------- engine actions ------------------------------------------ */
11044 case CA_SET_ENGINE_SCAN_MODE:
11046 InitPlayfieldScanMode(action_arg);
11056 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11058 int old_element = Feld[x][y];
11059 int new_element = GetElementFromGroupElement(element);
11060 int previous_move_direction = MovDir[x][y];
11061 #if USE_NEW_CUSTOM_VALUE
11062 int last_ce_value = CustomValue[x][y];
11064 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11065 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11066 boolean add_player_onto_element = (new_element_is_player &&
11067 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11068 /* this breaks SnakeBite when a snake is
11069 halfway through a door that closes */
11070 /* NOW FIXED AT LEVEL INIT IN files.c */
11071 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11073 IS_WALKABLE(old_element));
11076 /* check if element under the player changes from accessible to unaccessible
11077 (needed for special case of dropping element which then changes) */
11078 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11079 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11087 if (!add_player_onto_element)
11089 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11090 RemoveMovingField(x, y);
11094 Feld[x][y] = new_element;
11096 #if !USE_GFX_RESET_GFX_ANIMATION
11097 ResetGfxAnimation(x, y);
11098 ResetRandomAnimationValue(x, y);
11101 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11102 MovDir[x][y] = previous_move_direction;
11104 #if USE_NEW_CUSTOM_VALUE
11105 if (element_info[new_element].use_last_ce_value)
11106 CustomValue[x][y] = last_ce_value;
11109 InitField_WithBug1(x, y, FALSE);
11111 new_element = Feld[x][y]; /* element may have changed */
11113 #if USE_GFX_RESET_GFX_ANIMATION
11114 ResetGfxAnimation(x, y);
11115 ResetRandomAnimationValue(x, y);
11118 TEST_DrawLevelField(x, y);
11120 if (GFX_CRUMBLED(new_element))
11121 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11125 /* check if element under the player changes from accessible to unaccessible
11126 (needed for special case of dropping element which then changes) */
11127 /* (must be checked after creating new element for walkable group elements) */
11128 #if USE_FIX_KILLED_BY_NON_WALKABLE
11129 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11130 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11137 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11138 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11147 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11148 if (new_element_is_player)
11149 RelocatePlayer(x, y, new_element);
11152 ChangeCount[x][y]++; /* count number of changes in the same frame */
11154 TestIfBadThingTouchesPlayer(x, y);
11155 TestIfPlayerTouchesCustomElement(x, y);
11156 TestIfElementTouchesCustomElement(x, y);
11159 static void CreateField(int x, int y, int element)
11161 CreateFieldExt(x, y, element, FALSE);
11164 static void CreateElementFromChange(int x, int y, int element)
11166 element = GET_VALID_RUNTIME_ELEMENT(element);
11168 #if USE_STOP_CHANGED_ELEMENTS
11169 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11171 int old_element = Feld[x][y];
11173 /* prevent changed element from moving in same engine frame
11174 unless both old and new element can either fall or move */
11175 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11176 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11181 CreateFieldExt(x, y, element, TRUE);
11184 static boolean ChangeElement(int x, int y, int element, int page)
11186 struct ElementInfo *ei = &element_info[element];
11187 struct ElementChangeInfo *change = &ei->change_page[page];
11188 int ce_value = CustomValue[x][y];
11189 int ce_score = ei->collect_score;
11190 int target_element;
11191 int old_element = Feld[x][y];
11193 /* always use default change event to prevent running into a loop */
11194 if (ChangeEvent[x][y] == -1)
11195 ChangeEvent[x][y] = CE_DELAY;
11197 if (ChangeEvent[x][y] == CE_DELAY)
11199 /* reset actual trigger element, trigger player and action element */
11200 change->actual_trigger_element = EL_EMPTY;
11201 change->actual_trigger_player = EL_EMPTY;
11202 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11203 change->actual_trigger_side = CH_SIDE_NONE;
11204 change->actual_trigger_ce_value = 0;
11205 change->actual_trigger_ce_score = 0;
11208 /* do not change elements more than a specified maximum number of changes */
11209 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11212 ChangeCount[x][y]++; /* count number of changes in the same frame */
11214 if (change->explode)
11221 if (change->use_target_content)
11223 boolean complete_replace = TRUE;
11224 boolean can_replace[3][3];
11227 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11230 boolean is_walkable;
11231 boolean is_diggable;
11232 boolean is_collectible;
11233 boolean is_removable;
11234 boolean is_destructible;
11235 int ex = x + xx - 1;
11236 int ey = y + yy - 1;
11237 int content_element = change->target_content.e[xx][yy];
11240 can_replace[xx][yy] = TRUE;
11242 if (ex == x && ey == y) /* do not check changing element itself */
11245 if (content_element == EL_EMPTY_SPACE)
11247 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11252 if (!IN_LEV_FIELD(ex, ey))
11254 can_replace[xx][yy] = FALSE;
11255 complete_replace = FALSE;
11262 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11263 e = MovingOrBlocked2Element(ex, ey);
11265 is_empty = (IS_FREE(ex, ey) ||
11266 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11268 is_walkable = (is_empty || IS_WALKABLE(e));
11269 is_diggable = (is_empty || IS_DIGGABLE(e));
11270 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11271 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11272 is_removable = (is_diggable || is_collectible);
11274 can_replace[xx][yy] =
11275 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11276 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11277 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11278 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11279 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11280 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11281 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11283 if (!can_replace[xx][yy])
11284 complete_replace = FALSE;
11287 if (!change->only_if_complete || complete_replace)
11289 boolean something_has_changed = FALSE;
11291 if (change->only_if_complete && change->use_random_replace &&
11292 RND(100) < change->random_percentage)
11295 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11297 int ex = x + xx - 1;
11298 int ey = y + yy - 1;
11299 int content_element;
11301 if (can_replace[xx][yy] && (!change->use_random_replace ||
11302 RND(100) < change->random_percentage))
11304 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11305 RemoveMovingField(ex, ey);
11307 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11309 content_element = change->target_content.e[xx][yy];
11310 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11311 ce_value, ce_score);
11313 CreateElementFromChange(ex, ey, target_element);
11315 something_has_changed = TRUE;
11317 /* for symmetry reasons, freeze newly created border elements */
11318 if (ex != x || ey != y)
11319 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11323 if (something_has_changed)
11325 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11326 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11332 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11333 ce_value, ce_score);
11335 if (element == EL_DIAGONAL_GROWING ||
11336 element == EL_DIAGONAL_SHRINKING)
11338 target_element = Store[x][y];
11340 Store[x][y] = EL_EMPTY;
11343 CreateElementFromChange(x, y, target_element);
11345 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11346 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11349 /* this uses direct change before indirect change */
11350 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11355 #if USE_NEW_DELAYED_ACTION
11357 static void HandleElementChange(int x, int y, int page)
11359 int element = MovingOrBlocked2Element(x, y);
11360 struct ElementInfo *ei = &element_info[element];
11361 struct ElementChangeInfo *change = &ei->change_page[page];
11362 boolean handle_action_before_change = FALSE;
11365 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11366 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11369 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11370 x, y, element, element_info[element].token_name);
11371 printf("HandleElementChange(): This should never happen!\n");
11376 /* this can happen with classic bombs on walkable, changing elements */
11377 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11380 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11381 ChangeDelay[x][y] = 0;
11387 if (ChangeDelay[x][y] == 0) /* initialize element change */
11389 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11391 if (change->can_change)
11394 /* !!! not clear why graphic animation should be reset at all here !!! */
11395 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11396 #if USE_GFX_RESET_WHEN_NOT_MOVING
11397 /* when a custom element is about to change (for example by change delay),
11398 do not reset graphic animation when the custom element is moving */
11399 if (!IS_MOVING(x, y))
11402 ResetGfxAnimation(x, y);
11403 ResetRandomAnimationValue(x, y);
11407 if (change->pre_change_function)
11408 change->pre_change_function(x, y);
11412 ChangeDelay[x][y]--;
11414 if (ChangeDelay[x][y] != 0) /* continue element change */
11416 if (change->can_change)
11418 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11420 if (IS_ANIMATED(graphic))
11421 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11423 if (change->change_function)
11424 change->change_function(x, y);
11427 else /* finish element change */
11429 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11431 page = ChangePage[x][y];
11432 ChangePage[x][y] = -1;
11434 change = &ei->change_page[page];
11437 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11439 ChangeDelay[x][y] = 1; /* try change after next move step */
11440 ChangePage[x][y] = page; /* remember page to use for change */
11446 /* special case: set new level random seed before changing element */
11447 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11448 handle_action_before_change = TRUE;
11450 if (change->has_action && handle_action_before_change)
11451 ExecuteCustomElementAction(x, y, element, page);
11454 if (change->can_change)
11456 if (ChangeElement(x, y, element, page))
11458 if (change->post_change_function)
11459 change->post_change_function(x, y);
11463 if (change->has_action && !handle_action_before_change)
11464 ExecuteCustomElementAction(x, y, element, page);
11470 static void HandleElementChange(int x, int y, int page)
11472 int element = MovingOrBlocked2Element(x, y);
11473 struct ElementInfo *ei = &element_info[element];
11474 struct ElementChangeInfo *change = &ei->change_page[page];
11477 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11480 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11481 x, y, element, element_info[element].token_name);
11482 printf("HandleElementChange(): This should never happen!\n");
11487 /* this can happen with classic bombs on walkable, changing elements */
11488 if (!CAN_CHANGE(element))
11491 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11492 ChangeDelay[x][y] = 0;
11498 if (ChangeDelay[x][y] == 0) /* initialize element change */
11500 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11502 ResetGfxAnimation(x, y);
11503 ResetRandomAnimationValue(x, y);
11505 if (change->pre_change_function)
11506 change->pre_change_function(x, y);
11509 ChangeDelay[x][y]--;
11511 if (ChangeDelay[x][y] != 0) /* continue element change */
11513 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11515 if (IS_ANIMATED(graphic))
11516 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11518 if (change->change_function)
11519 change->change_function(x, y);
11521 else /* finish element change */
11523 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11525 page = ChangePage[x][y];
11526 ChangePage[x][y] = -1;
11528 change = &ei->change_page[page];
11531 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11533 ChangeDelay[x][y] = 1; /* try change after next move step */
11534 ChangePage[x][y] = page; /* remember page to use for change */
11539 if (ChangeElement(x, y, element, page))
11541 if (change->post_change_function)
11542 change->post_change_function(x, y);
11549 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11550 int trigger_element,
11552 int trigger_player,
11556 boolean change_done_any = FALSE;
11557 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11560 if (!(trigger_events[trigger_element][trigger_event]))
11564 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11565 trigger_event, recursion_loop_depth, recursion_loop_detected,
11566 recursion_loop_element, EL_NAME(recursion_loop_element));
11569 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11571 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11573 int element = EL_CUSTOM_START + i;
11574 boolean change_done = FALSE;
11577 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11578 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11581 for (p = 0; p < element_info[element].num_change_pages; p++)
11583 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11585 if (change->can_change_or_has_action &&
11586 change->has_event[trigger_event] &&
11587 change->trigger_side & trigger_side &&
11588 change->trigger_player & trigger_player &&
11589 change->trigger_page & trigger_page_bits &&
11590 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11592 change->actual_trigger_element = trigger_element;
11593 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11594 change->actual_trigger_player_bits = trigger_player;
11595 change->actual_trigger_side = trigger_side;
11596 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11597 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11600 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11601 element, EL_NAME(element), p);
11604 if ((change->can_change && !change_done) || change->has_action)
11608 SCAN_PLAYFIELD(x, y)
11610 if (Feld[x][y] == element)
11612 if (change->can_change && !change_done)
11614 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11615 /* if element already changed in this frame, not only prevent
11616 another element change (checked in ChangeElement()), but
11617 also prevent additional element actions for this element */
11619 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11620 !level.use_action_after_change_bug)
11625 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11626 element, EL_NAME(element), p);
11629 ChangeDelay[x][y] = 1;
11630 ChangeEvent[x][y] = trigger_event;
11632 HandleElementChange(x, y, p);
11634 #if USE_NEW_DELAYED_ACTION
11635 else if (change->has_action)
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638 /* if element already changed in this frame, not only prevent
11639 another element change (checked in ChangeElement()), but
11640 also prevent additional element actions for this element */
11642 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643 !level.use_action_after_change_bug)
11649 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11650 element, EL_NAME(element), p);
11653 ExecuteCustomElementAction(x, y, element, p);
11654 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11657 if (change->has_action)
11659 ExecuteCustomElementAction(x, y, element, p);
11660 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11666 if (change->can_change)
11668 change_done = TRUE;
11669 change_done_any = TRUE;
11672 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11673 element, EL_NAME(element), p);
11682 RECURSION_LOOP_DETECTION_END();
11684 return change_done_any;
11687 static boolean CheckElementChangeExt(int x, int y,
11689 int trigger_element,
11691 int trigger_player,
11694 boolean change_done = FALSE;
11697 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11698 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11701 if (Feld[x][y] == EL_BLOCKED)
11703 Blocked2Moving(x, y, &x, &y);
11704 element = Feld[x][y];
11708 /* check if element has already changed */
11709 if (Feld[x][y] != element)
11712 /* check if element has already changed or is about to change after moving */
11713 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11714 Feld[x][y] != element) ||
11716 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11717 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11718 ChangePage[x][y] != -1)))
11723 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11724 trigger_event, recursion_loop_depth, recursion_loop_detected,
11725 recursion_loop_element, EL_NAME(recursion_loop_element));
11728 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11731 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11734 for (p = 0; p < element_info[element].num_change_pages; p++)
11736 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11738 /* check trigger element for all events where the element that is checked
11739 for changing interacts with a directly adjacent element -- this is
11740 different to element changes that affect other elements to change on the
11741 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11742 boolean check_trigger_element =
11743 (trigger_event == CE_TOUCHING_X ||
11744 trigger_event == CE_HITTING_X ||
11745 trigger_event == CE_HIT_BY_X ||
11747 /* this one was forgotten until 3.2.3 */
11748 trigger_event == CE_DIGGING_X);
11751 if (change->can_change_or_has_action &&
11752 change->has_event[trigger_event] &&
11753 change->trigger_side & trigger_side &&
11754 change->trigger_player & trigger_player &&
11755 (!check_trigger_element ||
11756 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11758 change->actual_trigger_element = trigger_element;
11759 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11760 change->actual_trigger_player_bits = trigger_player;
11761 change->actual_trigger_side = trigger_side;
11762 change->actual_trigger_ce_value = CustomValue[x][y];
11763 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11765 /* special case: trigger element not at (x,y) position for some events */
11766 if (check_trigger_element)
11778 { 0, 0 }, { 0, 0 }, { 0, 0 },
11782 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11783 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11785 change->actual_trigger_ce_value = CustomValue[xx][yy];
11786 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11789 if (change->can_change && !change_done)
11791 ChangeDelay[x][y] = 1;
11792 ChangeEvent[x][y] = trigger_event;
11794 HandleElementChange(x, y, p);
11796 change_done = TRUE;
11798 #if USE_NEW_DELAYED_ACTION
11799 else if (change->has_action)
11801 ExecuteCustomElementAction(x, y, element, p);
11802 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11805 if (change->has_action)
11807 ExecuteCustomElementAction(x, y, element, p);
11808 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11814 RECURSION_LOOP_DETECTION_END();
11816 return change_done;
11819 static void PlayPlayerSound(struct PlayerInfo *player)
11821 int jx = player->jx, jy = player->jy;
11822 int sound_element = player->artwork_element;
11823 int last_action = player->last_action_waiting;
11824 int action = player->action_waiting;
11826 if (player->is_waiting)
11828 if (action != last_action)
11829 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11831 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11835 if (action != last_action)
11836 StopSound(element_info[sound_element].sound[last_action]);
11838 if (last_action == ACTION_SLEEPING)
11839 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11843 static void PlayAllPlayersSound()
11847 for (i = 0; i < MAX_PLAYERS; i++)
11848 if (stored_player[i].active)
11849 PlayPlayerSound(&stored_player[i]);
11852 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11854 boolean last_waiting = player->is_waiting;
11855 int move_dir = player->MovDir;
11857 player->dir_waiting = move_dir;
11858 player->last_action_waiting = player->action_waiting;
11862 if (!last_waiting) /* not waiting -> waiting */
11864 player->is_waiting = TRUE;
11866 player->frame_counter_bored =
11868 game.player_boring_delay_fixed +
11869 GetSimpleRandom(game.player_boring_delay_random);
11870 player->frame_counter_sleeping =
11872 game.player_sleeping_delay_fixed +
11873 GetSimpleRandom(game.player_sleeping_delay_random);
11875 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11878 if (game.player_sleeping_delay_fixed +
11879 game.player_sleeping_delay_random > 0 &&
11880 player->anim_delay_counter == 0 &&
11881 player->post_delay_counter == 0 &&
11882 FrameCounter >= player->frame_counter_sleeping)
11883 player->is_sleeping = TRUE;
11884 else if (game.player_boring_delay_fixed +
11885 game.player_boring_delay_random > 0 &&
11886 FrameCounter >= player->frame_counter_bored)
11887 player->is_bored = TRUE;
11889 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11890 player->is_bored ? ACTION_BORING :
11893 if (player->is_sleeping && player->use_murphy)
11895 /* special case for sleeping Murphy when leaning against non-free tile */
11897 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11898 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11899 !IS_MOVING(player->jx - 1, player->jy)))
11900 move_dir = MV_LEFT;
11901 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11902 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11903 !IS_MOVING(player->jx + 1, player->jy)))
11904 move_dir = MV_RIGHT;
11906 player->is_sleeping = FALSE;
11908 player->dir_waiting = move_dir;
11911 if (player->is_sleeping)
11913 if (player->num_special_action_sleeping > 0)
11915 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11917 int last_special_action = player->special_action_sleeping;
11918 int num_special_action = player->num_special_action_sleeping;
11919 int special_action =
11920 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11921 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11922 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11923 last_special_action + 1 : ACTION_SLEEPING);
11924 int special_graphic =
11925 el_act_dir2img(player->artwork_element, special_action, move_dir);
11927 player->anim_delay_counter =
11928 graphic_info[special_graphic].anim_delay_fixed +
11929 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11930 player->post_delay_counter =
11931 graphic_info[special_graphic].post_delay_fixed +
11932 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11934 player->special_action_sleeping = special_action;
11937 if (player->anim_delay_counter > 0)
11939 player->action_waiting = player->special_action_sleeping;
11940 player->anim_delay_counter--;
11942 else if (player->post_delay_counter > 0)
11944 player->post_delay_counter--;
11948 else if (player->is_bored)
11950 if (player->num_special_action_bored > 0)
11952 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11954 int special_action =
11955 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11956 int special_graphic =
11957 el_act_dir2img(player->artwork_element, special_action, move_dir);
11959 player->anim_delay_counter =
11960 graphic_info[special_graphic].anim_delay_fixed +
11961 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11962 player->post_delay_counter =
11963 graphic_info[special_graphic].post_delay_fixed +
11964 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11966 player->special_action_bored = special_action;
11969 if (player->anim_delay_counter > 0)
11971 player->action_waiting = player->special_action_bored;
11972 player->anim_delay_counter--;
11974 else if (player->post_delay_counter > 0)
11976 player->post_delay_counter--;
11981 else if (last_waiting) /* waiting -> not waiting */
11983 player->is_waiting = FALSE;
11984 player->is_bored = FALSE;
11985 player->is_sleeping = FALSE;
11987 player->frame_counter_bored = -1;
11988 player->frame_counter_sleeping = -1;
11990 player->anim_delay_counter = 0;
11991 player->post_delay_counter = 0;
11993 player->dir_waiting = player->MovDir;
11994 player->action_waiting = ACTION_DEFAULT;
11996 player->special_action_bored = ACTION_DEFAULT;
11997 player->special_action_sleeping = ACTION_DEFAULT;
12001 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12003 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12004 int left = player_action & JOY_LEFT;
12005 int right = player_action & JOY_RIGHT;
12006 int up = player_action & JOY_UP;
12007 int down = player_action & JOY_DOWN;
12008 int button1 = player_action & JOY_BUTTON_1;
12009 int button2 = player_action & JOY_BUTTON_2;
12010 int dx = (left ? -1 : right ? 1 : 0);
12011 int dy = (up ? -1 : down ? 1 : 0);
12013 if (!player->active || tape.pausing)
12019 snapped = SnapField(player, dx, dy);
12023 dropped = DropElement(player);
12025 moved = MovePlayer(player, dx, dy);
12028 if (tape.single_step && tape.recording && !tape.pausing)
12031 /* as it is called "single step mode", just return to pause mode when the
12032 player stopped moving after one tile (or never starts moving at all) */
12033 if (!player->is_moving)
12035 /* this is buggy: there are quite some cases where the single step mode
12036 does not return to pause mode (like pushing things that don't move
12037 or simply by trying to run against a wall) */
12038 if (button1 || (dropped && !moved))
12041 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12042 SnapField(player, 0, 0); /* stop snapping */
12046 SetPlayerWaiting(player, FALSE);
12048 return player_action;
12052 /* no actions for this player (no input at player's configured device) */
12054 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12055 SnapField(player, 0, 0);
12056 CheckGravityMovementWhenNotMoving(player);
12058 if (player->MovPos == 0)
12059 SetPlayerWaiting(player, TRUE);
12061 if (player->MovPos == 0) /* needed for tape.playing */
12062 player->is_moving = FALSE;
12064 player->is_dropping = FALSE;
12065 player->is_dropping_pressed = FALSE;
12066 player->drop_pressed_delay = 0;
12072 static void CheckLevelTime()
12076 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12077 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12079 if (level.native_em_level->lev->home == 0) /* all players at home */
12081 PlayerWins(local_player);
12083 AllPlayersGone = TRUE;
12085 level.native_em_level->lev->home = -1;
12088 if (level.native_em_level->ply[0]->alive == 0 &&
12089 level.native_em_level->ply[1]->alive == 0 &&
12090 level.native_em_level->ply[2]->alive == 0 &&
12091 level.native_em_level->ply[3]->alive == 0) /* all dead */
12092 AllPlayersGone = TRUE;
12094 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12096 if (game_sp.LevelSolved &&
12097 !game_sp.GameOver) /* game won */
12099 PlayerWins(local_player);
12101 game_sp.GameOver = TRUE;
12103 AllPlayersGone = TRUE;
12106 if (game_sp.GameOver) /* game lost */
12107 AllPlayersGone = TRUE;
12110 if (TimeFrames >= FRAMES_PER_SECOND)
12115 for (i = 0; i < MAX_PLAYERS; i++)
12117 struct PlayerInfo *player = &stored_player[i];
12119 if (SHIELD_ON(player))
12121 player->shield_normal_time_left--;
12123 if (player->shield_deadly_time_left > 0)
12124 player->shield_deadly_time_left--;
12128 if (!local_player->LevelSolved && !level.use_step_counter)
12136 if (TimeLeft <= 10 && setup.time_limit)
12137 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12140 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12142 DisplayGameControlValues();
12144 DrawGameValue_Time(TimeLeft);
12147 if (!TimeLeft && setup.time_limit)
12149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150 level.native_em_level->lev->killed_out_of_time = TRUE;
12152 for (i = 0; i < MAX_PLAYERS; i++)
12153 KillPlayer(&stored_player[i]);
12157 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12159 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12161 DisplayGameControlValues();
12164 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12165 DrawGameValue_Time(TimePlayed);
12168 level.native_em_level->lev->time =
12169 (level.time == 0 ? TimePlayed : TimeLeft);
12172 if (tape.recording || tape.playing)
12173 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12177 UpdateAndDisplayGameControlValues();
12179 UpdateGameDoorValues();
12180 DrawGameDoorValues();
12184 void AdvanceFrameAndPlayerCounters(int player_nr)
12188 /* advance frame counters (global frame counter and time frame counter) */
12192 /* advance player counters (counters for move delay, move animation etc.) */
12193 for (i = 0; i < MAX_PLAYERS; i++)
12195 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12196 int move_delay_value = stored_player[i].move_delay_value;
12197 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12199 if (!advance_player_counters) /* not all players may be affected */
12202 #if USE_NEW_PLAYER_ANIM
12203 if (move_frames == 0) /* less than one move per game frame */
12205 int stepsize = TILEX / move_delay_value;
12206 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12207 int count = (stored_player[i].is_moving ?
12208 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12210 if (count % delay == 0)
12215 stored_player[i].Frame += move_frames;
12217 if (stored_player[i].MovPos != 0)
12218 stored_player[i].StepFrame += move_frames;
12220 if (stored_player[i].move_delay > 0)
12221 stored_player[i].move_delay--;
12223 /* due to bugs in previous versions, counter must count up, not down */
12224 if (stored_player[i].push_delay != -1)
12225 stored_player[i].push_delay++;
12227 if (stored_player[i].drop_delay > 0)
12228 stored_player[i].drop_delay--;
12230 if (stored_player[i].is_dropping_pressed)
12231 stored_player[i].drop_pressed_delay++;
12235 void StartGameActions(boolean init_network_game, boolean record_tape,
12238 unsigned long new_random_seed = InitRND(random_seed);
12241 TapeStartRecording(new_random_seed);
12243 #if defined(NETWORK_AVALIABLE)
12244 if (init_network_game)
12246 SendToServer_StartPlaying();
12257 static unsigned long game_frame_delay = 0;
12258 unsigned long game_frame_delay_value;
12259 byte *recorded_player_action;
12260 byte summarized_player_action = 0;
12261 byte tape_action[MAX_PLAYERS];
12264 /* detect endless loops, caused by custom element programming */
12265 if (recursion_loop_detected && recursion_loop_depth == 0)
12267 char *message = getStringCat3("Internal Error ! Element ",
12268 EL_NAME(recursion_loop_element),
12269 " caused endless loop ! Quit the game ?");
12271 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12272 EL_NAME(recursion_loop_element));
12274 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12276 recursion_loop_detected = FALSE; /* if game should be continued */
12283 if (game.restart_level)
12284 StartGameActions(options.network, setup.autorecord, level.random_seed);
12286 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12289 if (level.native_em_level->lev->home == 0) /* all players at home */
12291 PlayerWins(local_player);
12293 AllPlayersGone = TRUE;
12295 level.native_em_level->lev->home = -1;
12298 if (level.native_em_level->ply[0]->alive == 0 &&
12299 level.native_em_level->ply[1]->alive == 0 &&
12300 level.native_em_level->ply[2]->alive == 0 &&
12301 level.native_em_level->ply[3]->alive == 0) /* all dead */
12302 AllPlayersGone = TRUE;
12304 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12306 if (game_sp.LevelSolved &&
12307 !game_sp.GameOver) /* game won */
12309 PlayerWins(local_player);
12311 game_sp.GameOver = TRUE;
12313 AllPlayersGone = TRUE;
12316 if (game_sp.GameOver) /* game lost */
12317 AllPlayersGone = TRUE;
12320 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12323 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12326 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12329 game_frame_delay_value =
12330 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12332 if (tape.playing && tape.warp_forward && !tape.pausing)
12333 game_frame_delay_value = 0;
12335 /* ---------- main game synchronization point ---------- */
12337 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12339 if (network_playing && !network_player_action_received)
12341 /* try to get network player actions in time */
12343 #if defined(NETWORK_AVALIABLE)
12344 /* last chance to get network player actions without main loop delay */
12345 HandleNetworking();
12348 /* game was quit by network peer */
12349 if (game_status != GAME_MODE_PLAYING)
12352 if (!network_player_action_received)
12353 return; /* failed to get network player actions in time */
12355 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12361 /* at this point we know that we really continue executing the game */
12363 network_player_action_received = FALSE;
12365 /* when playing tape, read previously recorded player input from tape data */
12366 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12369 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12374 if (tape.set_centered_player)
12376 game.centered_player_nr_next = tape.centered_player_nr_next;
12377 game.set_centered_player = TRUE;
12380 for (i = 0; i < MAX_PLAYERS; i++)
12382 summarized_player_action |= stored_player[i].action;
12384 if (!network_playing)
12385 stored_player[i].effective_action = stored_player[i].action;
12388 #if defined(NETWORK_AVALIABLE)
12389 if (network_playing)
12390 SendToServer_MovePlayer(summarized_player_action);
12393 if (!options.network && !setup.team_mode)
12394 local_player->effective_action = summarized_player_action;
12396 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12398 for (i = 0; i < MAX_PLAYERS; i++)
12399 stored_player[i].effective_action =
12400 (i == game.centered_player_nr ? summarized_player_action : 0);
12403 if (recorded_player_action != NULL)
12404 for (i = 0; i < MAX_PLAYERS; i++)
12405 stored_player[i].effective_action = recorded_player_action[i];
12407 for (i = 0; i < MAX_PLAYERS; i++)
12409 tape_action[i] = stored_player[i].effective_action;
12411 /* (this can only happen in the R'n'D game engine) */
12412 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12413 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12416 /* only record actions from input devices, but not programmed actions */
12417 if (tape.recording)
12418 TapeRecordAction(tape_action);
12420 #if USE_NEW_PLAYER_ASSIGNMENTS
12422 byte mapped_action[MAX_PLAYERS];
12424 for (i = 0; i < MAX_PLAYERS; i++)
12425 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12427 for (i = 0; i < MAX_PLAYERS; i++)
12428 stored_player[i].effective_action = mapped_action[i];
12432 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12434 GameActions_EM_Main();
12436 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12438 GameActions_SP_Main();
12446 void GameActions_EM_Main()
12448 byte effective_action[MAX_PLAYERS];
12449 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12452 for (i = 0; i < MAX_PLAYERS; i++)
12453 effective_action[i] = stored_player[i].effective_action;
12455 GameActions_EM(effective_action, warp_mode);
12459 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12462 void GameActions_SP_Main()
12464 byte effective_action[MAX_PLAYERS];
12465 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12468 for (i = 0; i < MAX_PLAYERS; i++)
12469 effective_action[i] = stored_player[i].effective_action;
12471 GameActions_SP(effective_action, warp_mode);
12475 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12478 void GameActions_RND()
12480 int magic_wall_x = 0, magic_wall_y = 0;
12481 int i, x, y, element, graphic;
12483 InitPlayfieldScanModeVars();
12485 #if USE_ONE_MORE_CHANGE_PER_FRAME
12486 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12488 SCAN_PLAYFIELD(x, y)
12490 ChangeCount[x][y] = 0;
12491 ChangeEvent[x][y] = -1;
12496 if (game.set_centered_player)
12498 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12500 /* switching to "all players" only possible if all players fit to screen */
12501 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12503 game.centered_player_nr_next = game.centered_player_nr;
12504 game.set_centered_player = FALSE;
12507 /* do not switch focus to non-existing (or non-active) player */
12508 if (game.centered_player_nr_next >= 0 &&
12509 !stored_player[game.centered_player_nr_next].active)
12511 game.centered_player_nr_next = game.centered_player_nr;
12512 game.set_centered_player = FALSE;
12516 if (game.set_centered_player &&
12517 ScreenMovPos == 0) /* screen currently aligned at tile position */
12521 if (game.centered_player_nr_next == -1)
12523 setScreenCenteredToAllPlayers(&sx, &sy);
12527 sx = stored_player[game.centered_player_nr_next].jx;
12528 sy = stored_player[game.centered_player_nr_next].jy;
12531 game.centered_player_nr = game.centered_player_nr_next;
12532 game.set_centered_player = FALSE;
12534 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12535 DrawGameDoorValues();
12538 for (i = 0; i < MAX_PLAYERS; i++)
12540 int actual_player_action = stored_player[i].effective_action;
12543 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12544 - rnd_equinox_tetrachloride 048
12545 - rnd_equinox_tetrachloride_ii 096
12546 - rnd_emanuel_schmieg 002
12547 - doctor_sloan_ww 001, 020
12549 if (stored_player[i].MovPos == 0)
12550 CheckGravityMovement(&stored_player[i]);
12553 /* overwrite programmed action with tape action */
12554 if (stored_player[i].programmed_action)
12555 actual_player_action = stored_player[i].programmed_action;
12557 PlayerActions(&stored_player[i], actual_player_action);
12559 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12562 ScrollScreen(NULL, SCROLL_GO_ON);
12564 /* for backwards compatibility, the following code emulates a fixed bug that
12565 occured when pushing elements (causing elements that just made their last
12566 pushing step to already (if possible) make their first falling step in the
12567 same game frame, which is bad); this code is also needed to use the famous
12568 "spring push bug" which is used in older levels and might be wanted to be
12569 used also in newer levels, but in this case the buggy pushing code is only
12570 affecting the "spring" element and no other elements */
12572 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12574 for (i = 0; i < MAX_PLAYERS; i++)
12576 struct PlayerInfo *player = &stored_player[i];
12577 int x = player->jx;
12578 int y = player->jy;
12580 if (player->active && player->is_pushing && player->is_moving &&
12582 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12583 Feld[x][y] == EL_SPRING))
12585 ContinueMoving(x, y);
12587 /* continue moving after pushing (this is actually a bug) */
12588 if (!IS_MOVING(x, y))
12589 Stop[x][y] = FALSE;
12595 debug_print_timestamp(0, "start main loop profiling");
12598 SCAN_PLAYFIELD(x, y)
12600 ChangeCount[x][y] = 0;
12601 ChangeEvent[x][y] = -1;
12603 /* this must be handled before main playfield loop */
12604 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12607 if (MovDelay[x][y] <= 0)
12611 #if USE_NEW_SNAP_DELAY
12612 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12615 if (MovDelay[x][y] <= 0)
12618 TEST_DrawLevelField(x, y);
12620 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12626 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12628 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12629 printf("GameActions(): This should never happen!\n");
12631 ChangePage[x][y] = -1;
12635 Stop[x][y] = FALSE;
12636 if (WasJustMoving[x][y] > 0)
12637 WasJustMoving[x][y]--;
12638 if (WasJustFalling[x][y] > 0)
12639 WasJustFalling[x][y]--;
12640 if (CheckCollision[x][y] > 0)
12641 CheckCollision[x][y]--;
12642 if (CheckImpact[x][y] > 0)
12643 CheckImpact[x][y]--;
12647 /* reset finished pushing action (not done in ContinueMoving() to allow
12648 continuous pushing animation for elements with zero push delay) */
12649 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12651 ResetGfxAnimation(x, y);
12652 TEST_DrawLevelField(x, y);
12656 if (IS_BLOCKED(x, y))
12660 Blocked2Moving(x, y, &oldx, &oldy);
12661 if (!IS_MOVING(oldx, oldy))
12663 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12664 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12665 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12666 printf("GameActions(): This should never happen!\n");
12673 debug_print_timestamp(0, "- time for pre-main loop:");
12676 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12677 SCAN_PLAYFIELD(x, y)
12679 element = Feld[x][y];
12680 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12685 int element2 = element;
12686 int graphic2 = graphic;
12688 int element2 = Feld[x][y];
12689 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12691 int last_gfx_frame = GfxFrame[x][y];
12693 if (graphic_info[graphic2].anim_global_sync)
12694 GfxFrame[x][y] = FrameCounter;
12695 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12696 GfxFrame[x][y] = CustomValue[x][y];
12697 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12698 GfxFrame[x][y] = element_info[element2].collect_score;
12699 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12700 GfxFrame[x][y] = ChangeDelay[x][y];
12702 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12703 DrawLevelGraphicAnimation(x, y, graphic2);
12706 ResetGfxFrame(x, y, TRUE);
12710 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12711 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12712 ResetRandomAnimationValue(x, y);
12716 SetRandomAnimationValue(x, y);
12720 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12723 #endif // -------------------- !!! TEST ONLY !!! --------------------
12726 debug_print_timestamp(0, "- time for TEST loop: -->");
12729 SCAN_PLAYFIELD(x, y)
12731 element = Feld[x][y];
12732 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12734 ResetGfxFrame(x, y, TRUE);
12736 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12737 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12738 ResetRandomAnimationValue(x, y);
12740 SetRandomAnimationValue(x, y);
12742 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12744 if (IS_INACTIVE(element))
12746 if (IS_ANIMATED(graphic))
12747 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12752 /* this may take place after moving, so 'element' may have changed */
12753 if (IS_CHANGING(x, y) &&
12754 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12756 int page = element_info[element].event_page_nr[CE_DELAY];
12759 HandleElementChange(x, y, page);
12761 if (CAN_CHANGE(element))
12762 HandleElementChange(x, y, page);
12764 if (HAS_ACTION(element))
12765 ExecuteCustomElementAction(x, y, element, page);
12768 element = Feld[x][y];
12769 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12772 #if 0 // ---------------------------------------------------------------------
12774 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12778 element = Feld[x][y];
12779 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12781 if (IS_ANIMATED(graphic) &&
12782 !IS_MOVING(x, y) &&
12784 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12786 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12787 TEST_DrawTwinkleOnField(x, y);
12789 else if (IS_MOVING(x, y))
12790 ContinueMoving(x, y);
12797 case EL_EM_EXIT_OPEN:
12798 case EL_SP_EXIT_OPEN:
12799 case EL_STEEL_EXIT_OPEN:
12800 case EL_EM_STEEL_EXIT_OPEN:
12801 case EL_SP_TERMINAL:
12802 case EL_SP_TERMINAL_ACTIVE:
12803 case EL_EXTRA_TIME:
12804 case EL_SHIELD_NORMAL:
12805 case EL_SHIELD_DEADLY:
12806 if (IS_ANIMATED(graphic))
12807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12810 case EL_DYNAMITE_ACTIVE:
12811 case EL_EM_DYNAMITE_ACTIVE:
12812 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12813 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12814 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12815 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12816 case EL_SP_DISK_RED_ACTIVE:
12817 CheckDynamite(x, y);
12820 case EL_AMOEBA_GROWING:
12821 AmoebeWaechst(x, y);
12824 case EL_AMOEBA_SHRINKING:
12825 AmoebaDisappearing(x, y);
12828 #if !USE_NEW_AMOEBA_CODE
12829 case EL_AMOEBA_WET:
12830 case EL_AMOEBA_DRY:
12831 case EL_AMOEBA_FULL:
12833 case EL_EMC_DRIPPER:
12834 AmoebeAbleger(x, y);
12838 case EL_GAME_OF_LIFE:
12843 case EL_EXIT_CLOSED:
12847 case EL_EM_EXIT_CLOSED:
12851 case EL_STEEL_EXIT_CLOSED:
12852 CheckExitSteel(x, y);
12855 case EL_EM_STEEL_EXIT_CLOSED:
12856 CheckExitSteelEM(x, y);
12859 case EL_SP_EXIT_CLOSED:
12863 case EL_EXPANDABLE_WALL_GROWING:
12864 case EL_EXPANDABLE_STEELWALL_GROWING:
12865 MauerWaechst(x, y);
12868 case EL_EXPANDABLE_WALL:
12869 case EL_EXPANDABLE_WALL_HORIZONTAL:
12870 case EL_EXPANDABLE_WALL_VERTICAL:
12871 case EL_EXPANDABLE_WALL_ANY:
12872 case EL_BD_EXPANDABLE_WALL:
12873 MauerAbleger(x, y);
12876 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12877 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12878 case EL_EXPANDABLE_STEELWALL_ANY:
12879 MauerAblegerStahl(x, y);
12883 CheckForDragon(x, y);
12889 case EL_ELEMENT_SNAPPING:
12890 case EL_DIAGONAL_SHRINKING:
12891 case EL_DIAGONAL_GROWING:
12894 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12896 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12901 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12907 #else // ---------------------------------------------------------------------
12909 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12913 element = Feld[x][y];
12914 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12916 if (IS_ANIMATED(graphic) &&
12917 !IS_MOVING(x, y) &&
12919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12921 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12922 TEST_DrawTwinkleOnField(x, y);
12924 else if ((element == EL_ACID ||
12925 element == EL_EXIT_OPEN ||
12926 element == EL_EM_EXIT_OPEN ||
12927 element == EL_SP_EXIT_OPEN ||
12928 element == EL_STEEL_EXIT_OPEN ||
12929 element == EL_EM_STEEL_EXIT_OPEN ||
12930 element == EL_SP_TERMINAL ||
12931 element == EL_SP_TERMINAL_ACTIVE ||
12932 element == EL_EXTRA_TIME ||
12933 element == EL_SHIELD_NORMAL ||
12934 element == EL_SHIELD_DEADLY) &&
12935 IS_ANIMATED(graphic))
12936 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12937 else if (IS_MOVING(x, y))
12938 ContinueMoving(x, y);
12939 else if (IS_ACTIVE_BOMB(element))
12940 CheckDynamite(x, y);
12941 else if (element == EL_AMOEBA_GROWING)
12942 AmoebeWaechst(x, y);
12943 else if (element == EL_AMOEBA_SHRINKING)
12944 AmoebaDisappearing(x, y);
12946 #if !USE_NEW_AMOEBA_CODE
12947 else if (IS_AMOEBALIVE(element))
12948 AmoebeAbleger(x, y);
12951 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12953 else if (element == EL_EXIT_CLOSED)
12955 else if (element == EL_EM_EXIT_CLOSED)
12957 else if (element == EL_STEEL_EXIT_CLOSED)
12958 CheckExitSteel(x, y);
12959 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12960 CheckExitSteelEM(x, y);
12961 else if (element == EL_SP_EXIT_CLOSED)
12963 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12964 element == EL_EXPANDABLE_STEELWALL_GROWING)
12965 MauerWaechst(x, y);
12966 else if (element == EL_EXPANDABLE_WALL ||
12967 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12968 element == EL_EXPANDABLE_WALL_VERTICAL ||
12969 element == EL_EXPANDABLE_WALL_ANY ||
12970 element == EL_BD_EXPANDABLE_WALL)
12971 MauerAbleger(x, y);
12972 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12973 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12974 element == EL_EXPANDABLE_STEELWALL_ANY)
12975 MauerAblegerStahl(x, y);
12976 else if (element == EL_FLAMES)
12977 CheckForDragon(x, y);
12978 else if (element == EL_EXPLOSION)
12979 ; /* drawing of correct explosion animation is handled separately */
12980 else if (element == EL_ELEMENT_SNAPPING ||
12981 element == EL_DIAGONAL_SHRINKING ||
12982 element == EL_DIAGONAL_GROWING)
12984 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12986 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12988 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12989 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12991 #endif // ---------------------------------------------------------------------
12993 if (IS_BELT_ACTIVE(element))
12994 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12996 if (game.magic_wall_active)
12998 int jx = local_player->jx, jy = local_player->jy;
13000 /* play the element sound at the position nearest to the player */
13001 if ((element == EL_MAGIC_WALL_FULL ||
13002 element == EL_MAGIC_WALL_ACTIVE ||
13003 element == EL_MAGIC_WALL_EMPTYING ||
13004 element == EL_BD_MAGIC_WALL_FULL ||
13005 element == EL_BD_MAGIC_WALL_ACTIVE ||
13006 element == EL_BD_MAGIC_WALL_EMPTYING ||
13007 element == EL_DC_MAGIC_WALL_FULL ||
13008 element == EL_DC_MAGIC_WALL_ACTIVE ||
13009 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13010 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13019 debug_print_timestamp(0, "- time for MAIN loop: -->");
13022 #if USE_NEW_AMOEBA_CODE
13023 /* new experimental amoeba growth stuff */
13024 if (!(FrameCounter % 8))
13026 static unsigned long random = 1684108901;
13028 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13030 x = RND(lev_fieldx);
13031 y = RND(lev_fieldy);
13032 element = Feld[x][y];
13034 if (!IS_PLAYER(x,y) &&
13035 (element == EL_EMPTY ||
13036 CAN_GROW_INTO(element) ||
13037 element == EL_QUICKSAND_EMPTY ||
13038 element == EL_QUICKSAND_FAST_EMPTY ||
13039 element == EL_ACID_SPLASH_LEFT ||
13040 element == EL_ACID_SPLASH_RIGHT))
13042 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13043 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13044 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13045 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13046 Feld[x][y] = EL_AMOEBA_DROP;
13049 random = random * 129 + 1;
13055 if (game.explosions_delayed)
13058 game.explosions_delayed = FALSE;
13060 SCAN_PLAYFIELD(x, y)
13062 element = Feld[x][y];
13064 if (ExplodeField[x][y])
13065 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13066 else if (element == EL_EXPLOSION)
13067 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13069 ExplodeField[x][y] = EX_TYPE_NONE;
13072 game.explosions_delayed = TRUE;
13075 if (game.magic_wall_active)
13077 if (!(game.magic_wall_time_left % 4))
13079 int element = Feld[magic_wall_x][magic_wall_y];
13081 if (element == EL_BD_MAGIC_WALL_FULL ||
13082 element == EL_BD_MAGIC_WALL_ACTIVE ||
13083 element == EL_BD_MAGIC_WALL_EMPTYING)
13084 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13085 else if (element == EL_DC_MAGIC_WALL_FULL ||
13086 element == EL_DC_MAGIC_WALL_ACTIVE ||
13087 element == EL_DC_MAGIC_WALL_EMPTYING)
13088 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13090 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13093 if (game.magic_wall_time_left > 0)
13095 game.magic_wall_time_left--;
13097 if (!game.magic_wall_time_left)
13099 SCAN_PLAYFIELD(x, y)
13101 element = Feld[x][y];
13103 if (element == EL_MAGIC_WALL_ACTIVE ||
13104 element == EL_MAGIC_WALL_FULL)
13106 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13107 TEST_DrawLevelField(x, y);
13109 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13110 element == EL_BD_MAGIC_WALL_FULL)
13112 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13113 TEST_DrawLevelField(x, y);
13115 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13116 element == EL_DC_MAGIC_WALL_FULL)
13118 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13119 TEST_DrawLevelField(x, y);
13123 game.magic_wall_active = FALSE;
13128 if (game.light_time_left > 0)
13130 game.light_time_left--;
13132 if (game.light_time_left == 0)
13133 RedrawAllLightSwitchesAndInvisibleElements();
13136 if (game.timegate_time_left > 0)
13138 game.timegate_time_left--;
13140 if (game.timegate_time_left == 0)
13141 CloseAllOpenTimegates();
13144 if (game.lenses_time_left > 0)
13146 game.lenses_time_left--;
13148 if (game.lenses_time_left == 0)
13149 RedrawAllInvisibleElementsForLenses();
13152 if (game.magnify_time_left > 0)
13154 game.magnify_time_left--;
13156 if (game.magnify_time_left == 0)
13157 RedrawAllInvisibleElementsForMagnifier();
13160 for (i = 0; i < MAX_PLAYERS; i++)
13162 struct PlayerInfo *player = &stored_player[i];
13164 if (SHIELD_ON(player))
13166 if (player->shield_deadly_time_left)
13167 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13168 else if (player->shield_normal_time_left)
13169 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13173 #if USE_DELAYED_GFX_REDRAW
13174 SCAN_PLAYFIELD(x, y)
13177 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13179 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13180 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13183 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13184 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13186 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13187 DrawLevelField(x, y);
13189 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13190 DrawLevelFieldCrumbled(x, y);
13192 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13193 DrawLevelFieldCrumbledNeighbours(x, y);
13195 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13196 DrawTwinkleOnField(x, y);
13199 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13206 PlayAllPlayersSound();
13208 if (options.debug) /* calculate frames per second */
13210 static unsigned long fps_counter = 0;
13211 static int fps_frames = 0;
13212 unsigned long fps_delay_ms = Counter() - fps_counter;
13216 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13218 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13221 fps_counter = Counter();
13224 redraw_mask |= REDRAW_FPS;
13227 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13229 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13231 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13233 local_player->show_envelope = 0;
13237 debug_print_timestamp(0, "stop main loop profiling ");
13238 printf("----------------------------------------------------------\n");
13241 /* use random number generator in every frame to make it less predictable */
13242 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13246 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13248 int min_x = x, min_y = y, max_x = x, max_y = y;
13251 for (i = 0; i < MAX_PLAYERS; i++)
13253 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13255 if (!stored_player[i].active || &stored_player[i] == player)
13258 min_x = MIN(min_x, jx);
13259 min_y = MIN(min_y, jy);
13260 max_x = MAX(max_x, jx);
13261 max_y = MAX(max_y, jy);
13264 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13267 static boolean AllPlayersInVisibleScreen()
13271 for (i = 0; i < MAX_PLAYERS; i++)
13273 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13275 if (!stored_player[i].active)
13278 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13285 void ScrollLevel(int dx, int dy)
13288 /* (directly solved in BlitBitmap() now) */
13289 static Bitmap *bitmap_db_field2 = NULL;
13290 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13297 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13298 /* only horizontal XOR vertical scroll direction allowed */
13299 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13304 /* (directly solved in BlitBitmap() now) */
13305 if (bitmap_db_field2 == NULL)
13306 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13308 /* needed when blitting directly to same bitmap -- should not be needed with
13309 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13310 BlitBitmap(drawto_field, bitmap_db_field2,
13311 FX + TILEX * (dx == -1) - softscroll_offset,
13312 FY + TILEY * (dy == -1) - softscroll_offset,
13313 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13314 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13315 FX + TILEX * (dx == 1) - softscroll_offset,
13316 FY + TILEY * (dy == 1) - softscroll_offset);
13317 BlitBitmap(bitmap_db_field2, drawto_field,
13318 FX + TILEX * (dx == 1) - softscroll_offset,
13319 FY + TILEY * (dy == 1) - softscroll_offset,
13320 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13321 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13322 FX + TILEX * (dx == 1) - softscroll_offset,
13323 FY + TILEY * (dy == 1) - softscroll_offset);
13328 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13329 int xsize = (BX2 - BX1 + 1);
13330 int ysize = (BY2 - BY1 + 1);
13331 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13332 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13333 int step = (start < end ? +1 : -1);
13335 for (i = start; i != end; i += step)
13337 BlitBitmap(drawto_field, drawto_field,
13338 FX + TILEX * (dx != 0 ? i + step : 0),
13339 FY + TILEY * (dy != 0 ? i + step : 0),
13340 TILEX * (dx != 0 ? 1 : xsize),
13341 TILEY * (dy != 0 ? 1 : ysize),
13342 FX + TILEX * (dx != 0 ? i : 0),
13343 FY + TILEY * (dy != 0 ? i : 0));
13348 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13350 BlitBitmap(drawto_field, drawto_field,
13351 FX + TILEX * (dx == -1) - softscroll_offset,
13352 FY + TILEY * (dy == -1) - softscroll_offset,
13353 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355 FX + TILEX * (dx == 1) - softscroll_offset,
13356 FY + TILEY * (dy == 1) - softscroll_offset);
13362 x = (dx == 1 ? BX1 : BX2);
13363 for (y = BY1; y <= BY2; y++)
13364 DrawScreenField(x, y);
13369 y = (dy == 1 ? BY1 : BY2);
13370 for (x = BX1; x <= BX2; x++)
13371 DrawScreenField(x, y);
13374 redraw_mask |= REDRAW_FIELD;
13377 static boolean canFallDown(struct PlayerInfo *player)
13379 int jx = player->jx, jy = player->jy;
13381 return (IN_LEV_FIELD(jx, jy + 1) &&
13382 (IS_FREE(jx, jy + 1) ||
13383 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13384 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13385 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13388 static boolean canPassField(int x, int y, int move_dir)
13390 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13391 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13392 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13393 int nextx = x + dx;
13394 int nexty = y + dy;
13395 int element = Feld[x][y];
13397 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13398 !CAN_MOVE(element) &&
13399 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13400 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13401 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13404 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13406 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13407 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13408 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13412 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13413 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13414 (IS_DIGGABLE(Feld[newx][newy]) ||
13415 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13416 canPassField(newx, newy, move_dir)));
13419 static void CheckGravityMovement(struct PlayerInfo *player)
13421 #if USE_PLAYER_GRAVITY
13422 if (player->gravity && !player->programmed_action)
13424 if (game.gravity && !player->programmed_action)
13427 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13428 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13429 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13430 int jx = player->jx, jy = player->jy;
13431 boolean player_is_moving_to_valid_field =
13432 (!player_is_snapping &&
13433 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13434 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13435 boolean player_can_fall_down = canFallDown(player);
13437 if (player_can_fall_down &&
13438 !player_is_moving_to_valid_field)
13439 player->programmed_action = MV_DOWN;
13443 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13445 return CheckGravityMovement(player);
13447 #if USE_PLAYER_GRAVITY
13448 if (player->gravity && !player->programmed_action)
13450 if (game.gravity && !player->programmed_action)
13453 int jx = player->jx, jy = player->jy;
13454 boolean field_under_player_is_free =
13455 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13456 boolean player_is_standing_on_valid_field =
13457 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13458 (IS_WALKABLE(Feld[jx][jy]) &&
13459 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13461 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13462 player->programmed_action = MV_DOWN;
13467 MovePlayerOneStep()
13468 -----------------------------------------------------------------------------
13469 dx, dy: direction (non-diagonal) to try to move the player to
13470 real_dx, real_dy: direction as read from input device (can be diagonal)
13473 boolean MovePlayerOneStep(struct PlayerInfo *player,
13474 int dx, int dy, int real_dx, int real_dy)
13476 int jx = player->jx, jy = player->jy;
13477 int new_jx = jx + dx, new_jy = jy + dy;
13478 #if !USE_FIXED_DONT_RUN_INTO
13482 boolean player_can_move = !player->cannot_move;
13484 if (!player->active || (!dx && !dy))
13485 return MP_NO_ACTION;
13487 player->MovDir = (dx < 0 ? MV_LEFT :
13488 dx > 0 ? MV_RIGHT :
13490 dy > 0 ? MV_DOWN : MV_NONE);
13492 if (!IN_LEV_FIELD(new_jx, new_jy))
13493 return MP_NO_ACTION;
13495 if (!player_can_move)
13497 if (player->MovPos == 0)
13499 player->is_moving = FALSE;
13500 player->is_digging = FALSE;
13501 player->is_collecting = FALSE;
13502 player->is_snapping = FALSE;
13503 player->is_pushing = FALSE;
13508 if (!options.network && game.centered_player_nr == -1 &&
13509 !AllPlayersInSight(player, new_jx, new_jy))
13510 return MP_NO_ACTION;
13512 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13513 return MP_NO_ACTION;
13516 #if !USE_FIXED_DONT_RUN_INTO
13517 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13519 /* (moved to DigField()) */
13520 if (player_can_move && DONT_RUN_INTO(element))
13522 if (element == EL_ACID && dx == 0 && dy == 1)
13524 SplashAcid(new_jx, new_jy);
13525 Feld[jx][jy] = EL_PLAYER_1;
13526 InitMovingField(jx, jy, MV_DOWN);
13527 Store[jx][jy] = EL_ACID;
13528 ContinueMoving(jx, jy);
13529 BuryPlayer(player);
13532 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13538 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13539 if (can_move != MP_MOVING)
13542 /* check if DigField() has caused relocation of the player */
13543 if (player->jx != jx || player->jy != jy)
13544 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13546 StorePlayer[jx][jy] = 0;
13547 player->last_jx = jx;
13548 player->last_jy = jy;
13549 player->jx = new_jx;
13550 player->jy = new_jy;
13551 StorePlayer[new_jx][new_jy] = player->element_nr;
13553 if (player->move_delay_value_next != -1)
13555 player->move_delay_value = player->move_delay_value_next;
13556 player->move_delay_value_next = -1;
13560 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13562 player->step_counter++;
13564 PlayerVisit[jx][jy] = FrameCounter;
13566 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13567 player->is_moving = TRUE;
13571 /* should better be called in MovePlayer(), but this breaks some tapes */
13572 ScrollPlayer(player, SCROLL_INIT);
13578 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13580 int jx = player->jx, jy = player->jy;
13581 int old_jx = jx, old_jy = jy;
13582 int moved = MP_NO_ACTION;
13584 if (!player->active)
13589 if (player->MovPos == 0)
13591 player->is_moving = FALSE;
13592 player->is_digging = FALSE;
13593 player->is_collecting = FALSE;
13594 player->is_snapping = FALSE;
13595 player->is_pushing = FALSE;
13601 if (player->move_delay > 0)
13604 player->move_delay = -1; /* set to "uninitialized" value */
13606 /* store if player is automatically moved to next field */
13607 player->is_auto_moving = (player->programmed_action != MV_NONE);
13609 /* remove the last programmed player action */
13610 player->programmed_action = 0;
13612 if (player->MovPos)
13614 /* should only happen if pre-1.2 tape recordings are played */
13615 /* this is only for backward compatibility */
13617 int original_move_delay_value = player->move_delay_value;
13620 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13624 /* scroll remaining steps with finest movement resolution */
13625 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13627 while (player->MovPos)
13629 ScrollPlayer(player, SCROLL_GO_ON);
13630 ScrollScreen(NULL, SCROLL_GO_ON);
13632 AdvanceFrameAndPlayerCounters(player->index_nr);
13638 player->move_delay_value = original_move_delay_value;
13641 player->is_active = FALSE;
13643 if (player->last_move_dir & MV_HORIZONTAL)
13645 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13646 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13650 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13651 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13654 #if USE_FIXED_BORDER_RUNNING_GFX
13655 if (!moved && !player->is_active)
13657 player->is_moving = FALSE;
13658 player->is_digging = FALSE;
13659 player->is_collecting = FALSE;
13660 player->is_snapping = FALSE;
13661 player->is_pushing = FALSE;
13669 if (moved & MP_MOVING && !ScreenMovPos &&
13670 (player->index_nr == game.centered_player_nr ||
13671 game.centered_player_nr == -1))
13673 if (moved & MP_MOVING && !ScreenMovPos &&
13674 (player == local_player || !options.network))
13677 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13678 int offset = game.scroll_delay_value;
13680 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13682 /* actual player has left the screen -- scroll in that direction */
13683 if (jx != old_jx) /* player has moved horizontally */
13684 scroll_x += (jx - old_jx);
13685 else /* player has moved vertically */
13686 scroll_y += (jy - old_jy);
13690 if (jx != old_jx) /* player has moved horizontally */
13692 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13693 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13694 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13696 /* don't scroll over playfield boundaries */
13697 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13698 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13700 /* don't scroll more than one field at a time */
13701 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13703 /* don't scroll against the player's moving direction */
13704 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13705 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13706 scroll_x = old_scroll_x;
13708 else /* player has moved vertically */
13710 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13711 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13712 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13714 /* don't scroll over playfield boundaries */
13715 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13716 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13718 /* don't scroll more than one field at a time */
13719 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13721 /* don't scroll against the player's moving direction */
13722 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13723 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13724 scroll_y = old_scroll_y;
13728 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13731 if (!options.network && game.centered_player_nr == -1 &&
13732 !AllPlayersInVisibleScreen())
13734 scroll_x = old_scroll_x;
13735 scroll_y = old_scroll_y;
13739 if (!options.network && !AllPlayersInVisibleScreen())
13741 scroll_x = old_scroll_x;
13742 scroll_y = old_scroll_y;
13747 ScrollScreen(player, SCROLL_INIT);
13748 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13753 player->StepFrame = 0;
13755 if (moved & MP_MOVING)
13757 if (old_jx != jx && old_jy == jy)
13758 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13759 else if (old_jx == jx && old_jy != jy)
13760 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13762 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13764 player->last_move_dir = player->MovDir;
13765 player->is_moving = TRUE;
13766 player->is_snapping = FALSE;
13767 player->is_switching = FALSE;
13768 player->is_dropping = FALSE;
13769 player->is_dropping_pressed = FALSE;
13770 player->drop_pressed_delay = 0;
13773 /* should better be called here than above, but this breaks some tapes */
13774 ScrollPlayer(player, SCROLL_INIT);
13779 CheckGravityMovementWhenNotMoving(player);
13781 player->is_moving = FALSE;
13783 /* at this point, the player is allowed to move, but cannot move right now
13784 (e.g. because of something blocking the way) -- ensure that the player
13785 is also allowed to move in the next frame (in old versions before 3.1.1,
13786 the player was forced to wait again for eight frames before next try) */
13788 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13789 player->move_delay = 0; /* allow direct movement in the next frame */
13792 if (player->move_delay == -1) /* not yet initialized by DigField() */
13793 player->move_delay = player->move_delay_value;
13795 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13797 TestIfPlayerTouchesBadThing(jx, jy);
13798 TestIfPlayerTouchesCustomElement(jx, jy);
13801 if (!player->active)
13802 RemovePlayer(player);
13807 void ScrollPlayer(struct PlayerInfo *player, int mode)
13809 int jx = player->jx, jy = player->jy;
13810 int last_jx = player->last_jx, last_jy = player->last_jy;
13811 int move_stepsize = TILEX / player->move_delay_value;
13813 #if USE_NEW_PLAYER_SPEED
13814 if (!player->active)
13817 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13820 if (!player->active || player->MovPos == 0)
13824 if (mode == SCROLL_INIT)
13826 player->actual_frame_counter = FrameCounter;
13827 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13829 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13830 Feld[last_jx][last_jy] == EL_EMPTY)
13832 int last_field_block_delay = 0; /* start with no blocking at all */
13833 int block_delay_adjustment = player->block_delay_adjustment;
13835 /* if player blocks last field, add delay for exactly one move */
13836 if (player->block_last_field)
13838 last_field_block_delay += player->move_delay_value;
13840 /* when blocking enabled, prevent moving up despite gravity */
13841 #if USE_PLAYER_GRAVITY
13842 if (player->gravity && player->MovDir == MV_UP)
13843 block_delay_adjustment = -1;
13845 if (game.gravity && player->MovDir == MV_UP)
13846 block_delay_adjustment = -1;
13850 /* add block delay adjustment (also possible when not blocking) */
13851 last_field_block_delay += block_delay_adjustment;
13853 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13854 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13857 #if USE_NEW_PLAYER_SPEED
13858 if (player->MovPos != 0) /* player has not yet reached destination */
13864 else if (!FrameReached(&player->actual_frame_counter, 1))
13867 #if USE_NEW_PLAYER_SPEED
13868 if (player->MovPos != 0)
13870 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13871 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13873 /* before DrawPlayer() to draw correct player graphic for this case */
13874 if (player->MovPos == 0)
13875 CheckGravityMovement(player);
13878 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13879 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13881 /* before DrawPlayer() to draw correct player graphic for this case */
13882 if (player->MovPos == 0)
13883 CheckGravityMovement(player);
13886 if (player->MovPos == 0) /* player reached destination field */
13888 if (player->move_delay_reset_counter > 0)
13890 player->move_delay_reset_counter--;
13892 if (player->move_delay_reset_counter == 0)
13894 /* continue with normal speed after quickly moving through gate */
13895 HALVE_PLAYER_SPEED(player);
13897 /* be able to make the next move without delay */
13898 player->move_delay = 0;
13902 player->last_jx = jx;
13903 player->last_jy = jy;
13905 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13906 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13908 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13910 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13911 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13913 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13915 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13916 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13918 DrawPlayer(player); /* needed here only to cleanup last field */
13919 RemovePlayer(player);
13921 if (local_player->friends_still_needed == 0 ||
13922 IS_SP_ELEMENT(Feld[jx][jy]))
13923 PlayerWins(player);
13926 /* this breaks one level: "machine", level 000 */
13928 int move_direction = player->MovDir;
13929 int enter_side = MV_DIR_OPPOSITE(move_direction);
13930 int leave_side = move_direction;
13931 int old_jx = last_jx;
13932 int old_jy = last_jy;
13933 int old_element = Feld[old_jx][old_jy];
13934 int new_element = Feld[jx][jy];
13936 if (IS_CUSTOM_ELEMENT(old_element))
13937 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13939 player->index_bit, leave_side);
13941 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13942 CE_PLAYER_LEAVES_X,
13943 player->index_bit, leave_side);
13945 if (IS_CUSTOM_ELEMENT(new_element))
13946 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13947 player->index_bit, enter_side);
13949 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13950 CE_PLAYER_ENTERS_X,
13951 player->index_bit, enter_side);
13953 #if USE_FIX_CE_ACTION_WITH_PLAYER
13954 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13955 CE_MOVE_OF_X, move_direction);
13957 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13958 CE_MOVE_OF_X, move_direction);
13962 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13964 TestIfPlayerTouchesBadThing(jx, jy);
13965 TestIfPlayerTouchesCustomElement(jx, jy);
13967 /* needed because pushed element has not yet reached its destination,
13968 so it would trigger a change event at its previous field location */
13969 if (!player->is_pushing)
13970 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13972 if (!player->active)
13973 RemovePlayer(player);
13976 if (!local_player->LevelSolved && level.use_step_counter)
13986 if (TimeLeft <= 10 && setup.time_limit)
13987 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13990 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13992 DisplayGameControlValues();
13994 DrawGameValue_Time(TimeLeft);
13997 if (!TimeLeft && setup.time_limit)
13998 for (i = 0; i < MAX_PLAYERS; i++)
13999 KillPlayer(&stored_player[i]);
14002 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14004 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14006 DisplayGameControlValues();
14009 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14010 DrawGameValue_Time(TimePlayed);
14014 if (tape.single_step && tape.recording && !tape.pausing &&
14015 !player->programmed_action)
14016 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14020 void ScrollScreen(struct PlayerInfo *player, int mode)
14022 static unsigned long screen_frame_counter = 0;
14024 if (mode == SCROLL_INIT)
14026 /* set scrolling step size according to actual player's moving speed */
14027 ScrollStepSize = TILEX / player->move_delay_value;
14029 screen_frame_counter = FrameCounter;
14030 ScreenMovDir = player->MovDir;
14031 ScreenMovPos = player->MovPos;
14032 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14035 else if (!FrameReached(&screen_frame_counter, 1))
14040 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14041 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14042 redraw_mask |= REDRAW_FIELD;
14045 ScreenMovDir = MV_NONE;
14048 void TestIfPlayerTouchesCustomElement(int x, int y)
14050 static int xy[4][2] =
14057 static int trigger_sides[4][2] =
14059 /* center side border side */
14060 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14061 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14062 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14063 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14065 static int touch_dir[4] =
14067 MV_LEFT | MV_RIGHT,
14072 int center_element = Feld[x][y]; /* should always be non-moving! */
14075 for (i = 0; i < NUM_DIRECTIONS; i++)
14077 int xx = x + xy[i][0];
14078 int yy = y + xy[i][1];
14079 int center_side = trigger_sides[i][0];
14080 int border_side = trigger_sides[i][1];
14081 int border_element;
14083 if (!IN_LEV_FIELD(xx, yy))
14086 if (IS_PLAYER(x, y)) /* player found at center element */
14088 struct PlayerInfo *player = PLAYERINFO(x, y);
14090 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14091 border_element = Feld[xx][yy]; /* may be moving! */
14092 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14093 border_element = Feld[xx][yy];
14094 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14095 border_element = MovingOrBlocked2Element(xx, yy);
14097 continue; /* center and border element do not touch */
14099 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14100 player->index_bit, border_side);
14101 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14102 CE_PLAYER_TOUCHES_X,
14103 player->index_bit, border_side);
14105 #if USE_FIX_CE_ACTION_WITH_PLAYER
14107 /* use player element that is initially defined in the level playfield,
14108 not the player element that corresponds to the runtime player number
14109 (example: a level that contains EL_PLAYER_3 as the only player would
14110 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14111 int player_element = PLAYERINFO(x, y)->initial_element;
14113 CheckElementChangeBySide(xx, yy, border_element, player_element,
14114 CE_TOUCHING_X, border_side);
14118 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14120 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14122 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14124 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14125 continue; /* center and border element do not touch */
14128 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14129 player->index_bit, center_side);
14130 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14131 CE_PLAYER_TOUCHES_X,
14132 player->index_bit, center_side);
14134 #if USE_FIX_CE_ACTION_WITH_PLAYER
14136 /* use player element that is initially defined in the level playfield,
14137 not the player element that corresponds to the runtime player number
14138 (example: a level that contains EL_PLAYER_3 as the only player would
14139 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14140 int player_element = PLAYERINFO(xx, yy)->initial_element;
14142 CheckElementChangeBySide(x, y, center_element, player_element,
14143 CE_TOUCHING_X, center_side);
14152 #if USE_ELEMENT_TOUCHING_BUGFIX
14154 void TestIfElementTouchesCustomElement(int x, int y)
14156 static int xy[4][2] =
14163 static int trigger_sides[4][2] =
14165 /* center side border side */
14166 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14167 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14168 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14169 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14171 static int touch_dir[4] =
14173 MV_LEFT | MV_RIGHT,
14178 boolean change_center_element = FALSE;
14179 int center_element = Feld[x][y]; /* should always be non-moving! */
14180 int border_element_old[NUM_DIRECTIONS];
14183 for (i = 0; i < NUM_DIRECTIONS; i++)
14185 int xx = x + xy[i][0];
14186 int yy = y + xy[i][1];
14187 int border_element;
14189 border_element_old[i] = -1;
14191 if (!IN_LEV_FIELD(xx, yy))
14194 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14195 border_element = Feld[xx][yy]; /* may be moving! */
14196 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14197 border_element = Feld[xx][yy];
14198 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14199 border_element = MovingOrBlocked2Element(xx, yy);
14201 continue; /* center and border element do not touch */
14203 border_element_old[i] = border_element;
14206 for (i = 0; i < NUM_DIRECTIONS; i++)
14208 int xx = x + xy[i][0];
14209 int yy = y + xy[i][1];
14210 int center_side = trigger_sides[i][0];
14211 int border_element = border_element_old[i];
14213 if (border_element == -1)
14216 /* check for change of border element */
14217 CheckElementChangeBySide(xx, yy, border_element, center_element,
14218 CE_TOUCHING_X, center_side);
14220 /* (center element cannot be player, so we dont have to check this here) */
14223 for (i = 0; i < NUM_DIRECTIONS; i++)
14225 int xx = x + xy[i][0];
14226 int yy = y + xy[i][1];
14227 int border_side = trigger_sides[i][1];
14228 int border_element = border_element_old[i];
14230 if (border_element == -1)
14233 /* check for change of center element (but change it only once) */
14234 if (!change_center_element)
14235 change_center_element =
14236 CheckElementChangeBySide(x, y, center_element, border_element,
14237 CE_TOUCHING_X, border_side);
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14240 if (IS_PLAYER(xx, yy))
14242 /* use player element that is initially defined in the level playfield,
14243 not the player element that corresponds to the runtime player number
14244 (example: a level that contains EL_PLAYER_3 as the only player would
14245 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14246 int player_element = PLAYERINFO(xx, yy)->initial_element;
14248 CheckElementChangeBySide(x, y, center_element, player_element,
14249 CE_TOUCHING_X, border_side);
14257 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14259 static int xy[4][2] =
14266 static int trigger_sides[4][2] =
14268 /* center side border side */
14269 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14270 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14271 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14272 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14274 static int touch_dir[4] =
14276 MV_LEFT | MV_RIGHT,
14281 boolean change_center_element = FALSE;
14282 int center_element = Feld[x][y]; /* should always be non-moving! */
14285 for (i = 0; i < NUM_DIRECTIONS; i++)
14287 int xx = x + xy[i][0];
14288 int yy = y + xy[i][1];
14289 int center_side = trigger_sides[i][0];
14290 int border_side = trigger_sides[i][1];
14291 int border_element;
14293 if (!IN_LEV_FIELD(xx, yy))
14296 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297 border_element = Feld[xx][yy]; /* may be moving! */
14298 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299 border_element = Feld[xx][yy];
14300 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14301 border_element = MovingOrBlocked2Element(xx, yy);
14303 continue; /* center and border element do not touch */
14305 /* check for change of center element (but change it only once) */
14306 if (!change_center_element)
14307 change_center_element =
14308 CheckElementChangeBySide(x, y, center_element, border_element,
14309 CE_TOUCHING_X, border_side);
14311 /* check for change of border element */
14312 CheckElementChangeBySide(xx, yy, border_element, center_element,
14313 CE_TOUCHING_X, center_side);
14319 void TestIfElementHitsCustomElement(int x, int y, int direction)
14321 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14322 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14323 int hitx = x + dx, hity = y + dy;
14324 int hitting_element = Feld[x][y];
14325 int touched_element;
14327 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14330 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14331 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14333 if (IN_LEV_FIELD(hitx, hity))
14335 int opposite_direction = MV_DIR_OPPOSITE(direction);
14336 int hitting_side = direction;
14337 int touched_side = opposite_direction;
14338 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14339 MovDir[hitx][hity] != direction ||
14340 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14346 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14347 CE_HITTING_X, touched_side);
14349 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14350 CE_HIT_BY_X, hitting_side);
14352 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14353 CE_HIT_BY_SOMETHING, opposite_direction);
14355 #if USE_FIX_CE_ACTION_WITH_PLAYER
14356 if (IS_PLAYER(hitx, hity))
14358 /* use player element that is initially defined in the level playfield,
14359 not the player element that corresponds to the runtime player number
14360 (example: a level that contains EL_PLAYER_3 as the only player would
14361 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14362 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14364 CheckElementChangeBySide(x, y, hitting_element, player_element,
14365 CE_HITTING_X, touched_side);
14371 /* "hitting something" is also true when hitting the playfield border */
14372 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14373 CE_HITTING_SOMETHING, direction);
14377 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14379 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14380 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14381 int hitx = x + dx, hity = y + dy;
14382 int hitting_element = Feld[x][y];
14383 int touched_element;
14385 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14386 !IS_FREE(hitx, hity) &&
14387 (!IS_MOVING(hitx, hity) ||
14388 MovDir[hitx][hity] != direction ||
14389 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14392 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14396 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14400 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14401 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14403 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14404 EP_CAN_SMASH_EVERYTHING, direction);
14406 if (IN_LEV_FIELD(hitx, hity))
14408 int opposite_direction = MV_DIR_OPPOSITE(direction);
14409 int hitting_side = direction;
14410 int touched_side = opposite_direction;
14412 int touched_element = MovingOrBlocked2Element(hitx, hity);
14415 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14416 MovDir[hitx][hity] != direction ||
14417 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14426 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14427 CE_SMASHED_BY_SOMETHING, opposite_direction);
14429 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14430 CE_OTHER_IS_SMASHING, touched_side);
14432 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14433 CE_OTHER_GETS_SMASHED, hitting_side);
14439 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14441 int i, kill_x = -1, kill_y = -1;
14443 int bad_element = -1;
14444 static int test_xy[4][2] =
14451 static int test_dir[4] =
14459 for (i = 0; i < NUM_DIRECTIONS; i++)
14461 int test_x, test_y, test_move_dir, test_element;
14463 test_x = good_x + test_xy[i][0];
14464 test_y = good_y + test_xy[i][1];
14466 if (!IN_LEV_FIELD(test_x, test_y))
14470 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14472 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14474 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14475 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14477 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14478 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14482 bad_element = test_element;
14488 if (kill_x != -1 || kill_y != -1)
14490 if (IS_PLAYER(good_x, good_y))
14492 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14494 if (player->shield_deadly_time_left > 0 &&
14495 !IS_INDESTRUCTIBLE(bad_element))
14496 Bang(kill_x, kill_y);
14497 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14498 KillPlayer(player);
14501 Bang(good_x, good_y);
14505 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14507 int i, kill_x = -1, kill_y = -1;
14508 int bad_element = Feld[bad_x][bad_y];
14509 static int test_xy[4][2] =
14516 static int touch_dir[4] =
14518 MV_LEFT | MV_RIGHT,
14523 static int test_dir[4] =
14531 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14534 for (i = 0; i < NUM_DIRECTIONS; i++)
14536 int test_x, test_y, test_move_dir, test_element;
14538 test_x = bad_x + test_xy[i][0];
14539 test_y = bad_y + test_xy[i][1];
14541 if (!IN_LEV_FIELD(test_x, test_y))
14545 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14547 test_element = Feld[test_x][test_y];
14549 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14550 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14552 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14553 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14555 /* good thing is player or penguin that does not move away */
14556 if (IS_PLAYER(test_x, test_y))
14558 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14560 if (bad_element == EL_ROBOT && player->is_moving)
14561 continue; /* robot does not kill player if he is moving */
14563 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14565 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14566 continue; /* center and border element do not touch */
14574 else if (test_element == EL_PENGUIN)
14584 if (kill_x != -1 || kill_y != -1)
14586 if (IS_PLAYER(kill_x, kill_y))
14588 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14590 if (player->shield_deadly_time_left > 0 &&
14591 !IS_INDESTRUCTIBLE(bad_element))
14592 Bang(bad_x, bad_y);
14593 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14594 KillPlayer(player);
14597 Bang(kill_x, kill_y);
14601 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14603 int bad_element = Feld[bad_x][bad_y];
14604 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14605 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14606 int test_x = bad_x + dx, test_y = bad_y + dy;
14607 int test_move_dir, test_element;
14608 int kill_x = -1, kill_y = -1;
14610 if (!IN_LEV_FIELD(test_x, test_y))
14614 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14616 test_element = Feld[test_x][test_y];
14618 if (test_move_dir != bad_move_dir)
14620 /* good thing can be player or penguin that does not move away */
14621 if (IS_PLAYER(test_x, test_y))
14623 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14625 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14626 player as being hit when he is moving towards the bad thing, because
14627 the "get hit by" condition would be lost after the player stops) */
14628 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14629 return; /* player moves away from bad thing */
14634 else if (test_element == EL_PENGUIN)
14641 if (kill_x != -1 || kill_y != -1)
14643 if (IS_PLAYER(kill_x, kill_y))
14645 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14647 if (player->shield_deadly_time_left > 0 &&
14648 !IS_INDESTRUCTIBLE(bad_element))
14649 Bang(bad_x, bad_y);
14650 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14651 KillPlayer(player);
14654 Bang(kill_x, kill_y);
14658 void TestIfPlayerTouchesBadThing(int x, int y)
14660 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14663 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14665 TestIfGoodThingHitsBadThing(x, y, move_dir);
14668 void TestIfBadThingTouchesPlayer(int x, int y)
14670 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14673 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14675 TestIfBadThingHitsGoodThing(x, y, move_dir);
14678 void TestIfFriendTouchesBadThing(int x, int y)
14680 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14683 void TestIfBadThingTouchesFriend(int x, int y)
14685 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14688 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14690 int i, kill_x = bad_x, kill_y = bad_y;
14691 static int xy[4][2] =
14699 for (i = 0; i < NUM_DIRECTIONS; i++)
14703 x = bad_x + xy[i][0];
14704 y = bad_y + xy[i][1];
14705 if (!IN_LEV_FIELD(x, y))
14708 element = Feld[x][y];
14709 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14710 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14718 if (kill_x != bad_x || kill_y != bad_y)
14719 Bang(bad_x, bad_y);
14722 void KillPlayer(struct PlayerInfo *player)
14724 int jx = player->jx, jy = player->jy;
14726 if (!player->active)
14730 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14731 player->killed, player->active, player->reanimated);
14734 /* the following code was introduced to prevent an infinite loop when calling
14736 -> CheckTriggeredElementChangeExt()
14737 -> ExecuteCustomElementAction()
14739 -> (infinitely repeating the above sequence of function calls)
14740 which occurs when killing the player while having a CE with the setting
14741 "kill player X when explosion of <player X>"; the solution using a new
14742 field "player->killed" was chosen for backwards compatibility, although
14743 clever use of the fields "player->active" etc. would probably also work */
14745 if (player->killed)
14749 player->killed = TRUE;
14751 /* remove accessible field at the player's position */
14752 Feld[jx][jy] = EL_EMPTY;
14754 /* deactivate shield (else Bang()/Explode() would not work right) */
14755 player->shield_normal_time_left = 0;
14756 player->shield_deadly_time_left = 0;
14759 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14760 player->killed, player->active, player->reanimated);
14766 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14767 player->killed, player->active, player->reanimated);
14770 #if USE_PLAYER_REANIMATION
14772 if (player->reanimated) /* killed player may have been reanimated */
14773 player->killed = player->reanimated = FALSE;
14775 BuryPlayer(player);
14777 if (player->killed) /* player may have been reanimated */
14778 BuryPlayer(player);
14781 BuryPlayer(player);
14785 static void KillPlayerUnlessEnemyProtected(int x, int y)
14787 if (!PLAYER_ENEMY_PROTECTED(x, y))
14788 KillPlayer(PLAYERINFO(x, y));
14791 static void KillPlayerUnlessExplosionProtected(int x, int y)
14793 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14794 KillPlayer(PLAYERINFO(x, y));
14797 void BuryPlayer(struct PlayerInfo *player)
14799 int jx = player->jx, jy = player->jy;
14801 if (!player->active)
14804 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14805 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14807 player->GameOver = TRUE;
14808 RemovePlayer(player);
14811 void RemovePlayer(struct PlayerInfo *player)
14813 int jx = player->jx, jy = player->jy;
14814 int i, found = FALSE;
14816 player->present = FALSE;
14817 player->active = FALSE;
14819 if (!ExplodeField[jx][jy])
14820 StorePlayer[jx][jy] = 0;
14822 if (player->is_moving)
14823 TEST_DrawLevelField(player->last_jx, player->last_jy);
14825 for (i = 0; i < MAX_PLAYERS; i++)
14826 if (stored_player[i].active)
14830 AllPlayersGone = TRUE;
14836 #if USE_NEW_SNAP_DELAY
14837 static void setFieldForSnapping(int x, int y, int element, int direction)
14839 struct ElementInfo *ei = &element_info[element];
14840 int direction_bit = MV_DIR_TO_BIT(direction);
14841 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14842 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14843 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14845 Feld[x][y] = EL_ELEMENT_SNAPPING;
14846 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14848 ResetGfxAnimation(x, y);
14850 GfxElement[x][y] = element;
14851 GfxAction[x][y] = action;
14852 GfxDir[x][y] = direction;
14853 GfxFrame[x][y] = -1;
14858 =============================================================================
14859 checkDiagonalPushing()
14860 -----------------------------------------------------------------------------
14861 check if diagonal input device direction results in pushing of object
14862 (by checking if the alternative direction is walkable, diggable, ...)
14863 =============================================================================
14866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14867 int x, int y, int real_dx, int real_dy)
14869 int jx, jy, dx, dy, xx, yy;
14871 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14874 /* diagonal direction: check alternative direction */
14879 xx = jx + (dx == 0 ? real_dx : 0);
14880 yy = jy + (dy == 0 ? real_dy : 0);
14882 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14886 =============================================================================
14888 -----------------------------------------------------------------------------
14889 x, y: field next to player (non-diagonal) to try to dig to
14890 real_dx, real_dy: direction as read from input device (can be diagonal)
14891 =============================================================================
14894 static int DigField(struct PlayerInfo *player,
14895 int oldx, int oldy, int x, int y,
14896 int real_dx, int real_dy, int mode)
14898 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14899 boolean player_was_pushing = player->is_pushing;
14900 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14901 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14902 int jx = oldx, jy = oldy;
14903 int dx = x - jx, dy = y - jy;
14904 int nextx = x + dx, nexty = y + dy;
14905 int move_direction = (dx == -1 ? MV_LEFT :
14906 dx == +1 ? MV_RIGHT :
14908 dy == +1 ? MV_DOWN : MV_NONE);
14909 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14910 int dig_side = MV_DIR_OPPOSITE(move_direction);
14911 int old_element = Feld[jx][jy];
14912 #if USE_FIXED_DONT_RUN_INTO
14913 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14919 if (is_player) /* function can also be called by EL_PENGUIN */
14921 if (player->MovPos == 0)
14923 player->is_digging = FALSE;
14924 player->is_collecting = FALSE;
14927 if (player->MovPos == 0) /* last pushing move finished */
14928 player->is_pushing = FALSE;
14930 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14932 player->is_switching = FALSE;
14933 player->push_delay = -1;
14935 return MP_NO_ACTION;
14939 #if !USE_FIXED_DONT_RUN_INTO
14940 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14941 return MP_NO_ACTION;
14944 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14945 old_element = Back[jx][jy];
14947 /* in case of element dropped at player position, check background */
14948 else if (Back[jx][jy] != EL_EMPTY &&
14949 game.engine_version >= VERSION_IDENT(2,2,0,0))
14950 old_element = Back[jx][jy];
14952 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14953 return MP_NO_ACTION; /* field has no opening in this direction */
14955 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14956 return MP_NO_ACTION; /* field has no opening in this direction */
14958 #if USE_FIXED_DONT_RUN_INTO
14959 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14963 Feld[jx][jy] = player->artwork_element;
14964 InitMovingField(jx, jy, MV_DOWN);
14965 Store[jx][jy] = EL_ACID;
14966 ContinueMoving(jx, jy);
14967 BuryPlayer(player);
14969 return MP_DONT_RUN_INTO;
14973 #if USE_FIXED_DONT_RUN_INTO
14974 if (player_can_move && DONT_RUN_INTO(element))
14976 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14978 return MP_DONT_RUN_INTO;
14982 #if USE_FIXED_DONT_RUN_INTO
14983 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14984 return MP_NO_ACTION;
14987 #if !USE_FIXED_DONT_RUN_INTO
14988 element = Feld[x][y];
14991 collect_count = element_info[element].collect_count_initial;
14993 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14994 return MP_NO_ACTION;
14996 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14997 player_can_move = player_can_move_or_snap;
14999 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15000 game.engine_version >= VERSION_IDENT(2,2,0,0))
15002 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15003 player->index_bit, dig_side);
15004 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15005 player->index_bit, dig_side);
15007 if (element == EL_DC_LANDMINE)
15010 if (Feld[x][y] != element) /* field changed by snapping */
15013 return MP_NO_ACTION;
15016 #if USE_PLAYER_GRAVITY
15017 if (player->gravity && is_player && !player->is_auto_moving &&
15018 canFallDown(player) && move_direction != MV_DOWN &&
15019 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15020 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15022 if (game.gravity && is_player && !player->is_auto_moving &&
15023 canFallDown(player) && move_direction != MV_DOWN &&
15024 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15025 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15028 if (player_can_move &&
15029 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15031 int sound_element = SND_ELEMENT(element);
15032 int sound_action = ACTION_WALKING;
15034 if (IS_RND_GATE(element))
15036 if (!player->key[RND_GATE_NR(element)])
15037 return MP_NO_ACTION;
15039 else if (IS_RND_GATE_GRAY(element))
15041 if (!player->key[RND_GATE_GRAY_NR(element)])
15042 return MP_NO_ACTION;
15044 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15046 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15047 return MP_NO_ACTION;
15049 else if (element == EL_EXIT_OPEN ||
15050 element == EL_EM_EXIT_OPEN ||
15052 element == EL_EM_EXIT_OPENING ||
15054 element == EL_STEEL_EXIT_OPEN ||
15055 element == EL_EM_STEEL_EXIT_OPEN ||
15057 element == EL_EM_STEEL_EXIT_OPENING ||
15059 element == EL_SP_EXIT_OPEN ||
15060 element == EL_SP_EXIT_OPENING)
15062 sound_action = ACTION_PASSING; /* player is passing exit */
15064 else if (element == EL_EMPTY)
15066 sound_action = ACTION_MOVING; /* nothing to walk on */
15069 /* play sound from background or player, whatever is available */
15070 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15071 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15073 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15075 else if (player_can_move &&
15076 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15078 if (!ACCESS_FROM(element, opposite_direction))
15079 return MP_NO_ACTION; /* field not accessible from this direction */
15081 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15082 return MP_NO_ACTION;
15084 if (IS_EM_GATE(element))
15086 if (!player->key[EM_GATE_NR(element)])
15087 return MP_NO_ACTION;
15089 else if (IS_EM_GATE_GRAY(element))
15091 if (!player->key[EM_GATE_GRAY_NR(element)])
15092 return MP_NO_ACTION;
15094 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15096 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15097 return MP_NO_ACTION;
15099 else if (IS_EMC_GATE(element))
15101 if (!player->key[EMC_GATE_NR(element)])
15102 return MP_NO_ACTION;
15104 else if (IS_EMC_GATE_GRAY(element))
15106 if (!player->key[EMC_GATE_GRAY_NR(element)])
15107 return MP_NO_ACTION;
15109 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15111 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15112 return MP_NO_ACTION;
15114 else if (element == EL_DC_GATE_WHITE ||
15115 element == EL_DC_GATE_WHITE_GRAY ||
15116 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15118 if (player->num_white_keys == 0)
15119 return MP_NO_ACTION;
15121 player->num_white_keys--;
15123 else if (IS_SP_PORT(element))
15125 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15126 element == EL_SP_GRAVITY_PORT_RIGHT ||
15127 element == EL_SP_GRAVITY_PORT_UP ||
15128 element == EL_SP_GRAVITY_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130 player->gravity = !player->gravity;
15132 game.gravity = !game.gravity;
15134 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15135 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15136 element == EL_SP_GRAVITY_ON_PORT_UP ||
15137 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139 player->gravity = TRUE;
15141 game.gravity = TRUE;
15143 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15144 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15145 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15146 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15147 #if USE_PLAYER_GRAVITY
15148 player->gravity = FALSE;
15150 game.gravity = FALSE;
15154 /* automatically move to the next field with double speed */
15155 player->programmed_action = move_direction;
15157 if (player->move_delay_reset_counter == 0)
15159 player->move_delay_reset_counter = 2; /* two double speed steps */
15161 DOUBLE_PLAYER_SPEED(player);
15164 PlayLevelSoundAction(x, y, ACTION_PASSING);
15166 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15170 if (mode != DF_SNAP)
15172 GfxElement[x][y] = GFX_ELEMENT(element);
15173 player->is_digging = TRUE;
15176 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15178 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15179 player->index_bit, dig_side);
15181 if (mode == DF_SNAP)
15183 #if USE_NEW_SNAP_DELAY
15184 if (level.block_snap_field)
15185 setFieldForSnapping(x, y, element, move_direction);
15187 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15189 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15192 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193 player->index_bit, dig_side);
15196 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15200 if (is_player && mode != DF_SNAP)
15202 GfxElement[x][y] = element;
15203 player->is_collecting = TRUE;
15206 if (element == EL_SPEED_PILL)
15208 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15210 else if (element == EL_EXTRA_TIME && level.time > 0)
15212 TimeLeft += level.extra_time;
15215 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15217 DisplayGameControlValues();
15219 DrawGameValue_Time(TimeLeft);
15222 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15224 player->shield_normal_time_left += level.shield_normal_time;
15225 if (element == EL_SHIELD_DEADLY)
15226 player->shield_deadly_time_left += level.shield_deadly_time;
15228 else if (element == EL_DYNAMITE ||
15229 element == EL_EM_DYNAMITE ||
15230 element == EL_SP_DISK_RED)
15232 if (player->inventory_size < MAX_INVENTORY_SIZE)
15233 player->inventory_element[player->inventory_size++] = element;
15235 DrawGameDoorValues();
15237 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15239 player->dynabomb_count++;
15240 player->dynabombs_left++;
15242 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15244 player->dynabomb_size++;
15246 else if (element == EL_DYNABOMB_INCREASE_POWER)
15248 player->dynabomb_xl = TRUE;
15250 else if (IS_KEY(element))
15252 player->key[KEY_NR(element)] = TRUE;
15254 DrawGameDoorValues();
15256 else if (element == EL_DC_KEY_WHITE)
15258 player->num_white_keys++;
15260 /* display white keys? */
15261 /* DrawGameDoorValues(); */
15263 else if (IS_ENVELOPE(element))
15265 player->show_envelope = element;
15267 else if (element == EL_EMC_LENSES)
15269 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15271 RedrawAllInvisibleElementsForLenses();
15273 else if (element == EL_EMC_MAGNIFIER)
15275 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15277 RedrawAllInvisibleElementsForMagnifier();
15279 else if (IS_DROPPABLE(element) ||
15280 IS_THROWABLE(element)) /* can be collected and dropped */
15284 if (collect_count == 0)
15285 player->inventory_infinite_element = element;
15287 for (i = 0; i < collect_count; i++)
15288 if (player->inventory_size < MAX_INVENTORY_SIZE)
15289 player->inventory_element[player->inventory_size++] = element;
15291 DrawGameDoorValues();
15293 else if (collect_count > 0)
15295 local_player->gems_still_needed -= collect_count;
15296 if (local_player->gems_still_needed < 0)
15297 local_player->gems_still_needed = 0;
15300 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15302 DisplayGameControlValues();
15304 DrawGameValue_Emeralds(local_player->gems_still_needed);
15308 RaiseScoreElement(element);
15309 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15312 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15313 player->index_bit, dig_side);
15315 if (mode == DF_SNAP)
15317 #if USE_NEW_SNAP_DELAY
15318 if (level.block_snap_field)
15319 setFieldForSnapping(x, y, element, move_direction);
15321 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15323 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15326 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327 player->index_bit, dig_side);
15330 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15332 if (mode == DF_SNAP && element != EL_BD_ROCK)
15333 return MP_NO_ACTION;
15335 if (CAN_FALL(element) && dy)
15336 return MP_NO_ACTION;
15338 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15339 !(element == EL_SPRING && level.use_spring_bug))
15340 return MP_NO_ACTION;
15342 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15343 ((move_direction & MV_VERTICAL &&
15344 ((element_info[element].move_pattern & MV_LEFT &&
15345 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15346 (element_info[element].move_pattern & MV_RIGHT &&
15347 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15348 (move_direction & MV_HORIZONTAL &&
15349 ((element_info[element].move_pattern & MV_UP &&
15350 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15351 (element_info[element].move_pattern & MV_DOWN &&
15352 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15353 return MP_NO_ACTION;
15355 /* do not push elements already moving away faster than player */
15356 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15357 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15358 return MP_NO_ACTION;
15360 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15362 if (player->push_delay_value == -1 || !player_was_pushing)
15363 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15365 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15367 if (player->push_delay_value == -1)
15368 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15370 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15372 if (!player->is_pushing)
15373 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15376 player->is_pushing = TRUE;
15377 player->is_active = TRUE;
15379 if (!(IN_LEV_FIELD(nextx, nexty) &&
15380 (IS_FREE(nextx, nexty) ||
15381 (IS_SB_ELEMENT(element) &&
15382 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15383 (IS_CUSTOM_ELEMENT(element) &&
15384 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15385 return MP_NO_ACTION;
15387 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15388 return MP_NO_ACTION;
15390 if (player->push_delay == -1) /* new pushing; restart delay */
15391 player->push_delay = 0;
15393 if (player->push_delay < player->push_delay_value &&
15394 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15395 element != EL_SPRING && element != EL_BALLOON)
15397 /* make sure that there is no move delay before next try to push */
15398 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15399 player->move_delay = 0;
15401 return MP_NO_ACTION;
15404 if (IS_CUSTOM_ELEMENT(element) &&
15405 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15407 if (!DigFieldByCE(nextx, nexty, element))
15408 return MP_NO_ACTION;
15411 if (IS_SB_ELEMENT(element))
15413 if (element == EL_SOKOBAN_FIELD_FULL)
15415 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15416 local_player->sokobanfields_still_needed++;
15419 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15421 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15422 local_player->sokobanfields_still_needed--;
15425 Feld[x][y] = EL_SOKOBAN_OBJECT;
15427 if (Back[x][y] == Back[nextx][nexty])
15428 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15429 else if (Back[x][y] != 0)
15430 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15433 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15437 if (local_player->sokobanfields_still_needed == 0 &&
15438 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15440 if (local_player->sokobanfields_still_needed == 0 &&
15441 game.emulation == EMU_SOKOBAN)
15444 PlayerWins(player);
15446 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15450 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15452 InitMovingField(x, y, move_direction);
15453 GfxAction[x][y] = ACTION_PUSHING;
15455 if (mode == DF_SNAP)
15456 ContinueMoving(x, y);
15458 MovPos[x][y] = (dx != 0 ? dx : dy);
15460 Pushed[x][y] = TRUE;
15461 Pushed[nextx][nexty] = TRUE;
15463 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15464 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15466 player->push_delay_value = -1; /* get new value later */
15468 /* check for element change _after_ element has been pushed */
15469 if (game.use_change_when_pushing_bug)
15471 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15472 player->index_bit, dig_side);
15473 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15474 player->index_bit, dig_side);
15477 else if (IS_SWITCHABLE(element))
15479 if (PLAYER_SWITCHING(player, x, y))
15481 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15482 player->index_bit, dig_side);
15487 player->is_switching = TRUE;
15488 player->switch_x = x;
15489 player->switch_y = y;
15491 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15493 if (element == EL_ROBOT_WHEEL)
15495 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15499 game.robot_wheel_active = TRUE;
15501 TEST_DrawLevelField(x, y);
15503 else if (element == EL_SP_TERMINAL)
15507 SCAN_PLAYFIELD(xx, yy)
15509 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15511 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15512 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15515 else if (IS_BELT_SWITCH(element))
15517 ToggleBeltSwitch(x, y);
15519 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15520 element == EL_SWITCHGATE_SWITCH_DOWN ||
15521 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15522 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15524 ToggleSwitchgateSwitch(x, y);
15526 else if (element == EL_LIGHT_SWITCH ||
15527 element == EL_LIGHT_SWITCH_ACTIVE)
15529 ToggleLightSwitch(x, y);
15531 else if (element == EL_TIMEGATE_SWITCH ||
15532 element == EL_DC_TIMEGATE_SWITCH)
15534 ActivateTimegateSwitch(x, y);
15536 else if (element == EL_BALLOON_SWITCH_LEFT ||
15537 element == EL_BALLOON_SWITCH_RIGHT ||
15538 element == EL_BALLOON_SWITCH_UP ||
15539 element == EL_BALLOON_SWITCH_DOWN ||
15540 element == EL_BALLOON_SWITCH_NONE ||
15541 element == EL_BALLOON_SWITCH_ANY)
15543 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15544 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15545 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15546 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15547 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15550 else if (element == EL_LAMP)
15552 Feld[x][y] = EL_LAMP_ACTIVE;
15553 local_player->lights_still_needed--;
15555 ResetGfxAnimation(x, y);
15556 TEST_DrawLevelField(x, y);
15558 else if (element == EL_TIME_ORB_FULL)
15560 Feld[x][y] = EL_TIME_ORB_EMPTY;
15562 if (level.time > 0 || level.use_time_orb_bug)
15564 TimeLeft += level.time_orb_time;
15567 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15569 DisplayGameControlValues();
15571 DrawGameValue_Time(TimeLeft);
15575 ResetGfxAnimation(x, y);
15576 TEST_DrawLevelField(x, y);
15578 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15579 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15583 game.ball_state = !game.ball_state;
15585 SCAN_PLAYFIELD(xx, yy)
15587 int e = Feld[xx][yy];
15589 if (game.ball_state)
15591 if (e == EL_EMC_MAGIC_BALL)
15592 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15593 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15594 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15598 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15599 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15600 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15601 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15606 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15607 player->index_bit, dig_side);
15609 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15610 player->index_bit, dig_side);
15612 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15613 player->index_bit, dig_side);
15619 if (!PLAYER_SWITCHING(player, x, y))
15621 player->is_switching = TRUE;
15622 player->switch_x = x;
15623 player->switch_y = y;
15625 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15626 player->index_bit, dig_side);
15627 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15628 player->index_bit, dig_side);
15630 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15631 player->index_bit, dig_side);
15632 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15633 player->index_bit, dig_side);
15636 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15637 player->index_bit, dig_side);
15638 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15639 player->index_bit, dig_side);
15641 return MP_NO_ACTION;
15644 player->push_delay = -1;
15646 if (is_player) /* function can also be called by EL_PENGUIN */
15648 if (Feld[x][y] != element) /* really digged/collected something */
15650 player->is_collecting = !player->is_digging;
15651 player->is_active = TRUE;
15658 static boolean DigFieldByCE(int x, int y, int digging_element)
15660 int element = Feld[x][y];
15662 if (!IS_FREE(x, y))
15664 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15665 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15668 /* no element can dig solid indestructible elements */
15669 if (IS_INDESTRUCTIBLE(element) &&
15670 !IS_DIGGABLE(element) &&
15671 !IS_COLLECTIBLE(element))
15674 if (AmoebaNr[x][y] &&
15675 (element == EL_AMOEBA_FULL ||
15676 element == EL_BD_AMOEBA ||
15677 element == EL_AMOEBA_GROWING))
15679 AmoebaCnt[AmoebaNr[x][y]]--;
15680 AmoebaCnt2[AmoebaNr[x][y]]--;
15683 if (IS_MOVING(x, y))
15684 RemoveMovingField(x, y);
15688 TEST_DrawLevelField(x, y);
15691 /* if digged element was about to explode, prevent the explosion */
15692 ExplodeField[x][y] = EX_TYPE_NONE;
15694 PlayLevelSoundAction(x, y, action);
15697 Store[x][y] = EL_EMPTY;
15700 /* this makes it possible to leave the removed element again */
15701 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15702 Store[x][y] = element;
15704 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15706 int move_leave_element = element_info[digging_element].move_leave_element;
15708 /* this makes it possible to leave the removed element again */
15709 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15710 element : move_leave_element);
15717 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15719 int jx = player->jx, jy = player->jy;
15720 int x = jx + dx, y = jy + dy;
15721 int snap_direction = (dx == -1 ? MV_LEFT :
15722 dx == +1 ? MV_RIGHT :
15724 dy == +1 ? MV_DOWN : MV_NONE);
15725 boolean can_continue_snapping = (level.continuous_snapping &&
15726 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15728 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15731 if (!player->active || !IN_LEV_FIELD(x, y))
15739 if (player->MovPos == 0)
15740 player->is_pushing = FALSE;
15742 player->is_snapping = FALSE;
15744 if (player->MovPos == 0)
15746 player->is_moving = FALSE;
15747 player->is_digging = FALSE;
15748 player->is_collecting = FALSE;
15754 #if USE_NEW_CONTINUOUS_SNAPPING
15755 /* prevent snapping with already pressed snap key when not allowed */
15756 if (player->is_snapping && !can_continue_snapping)
15759 if (player->is_snapping)
15763 player->MovDir = snap_direction;
15765 if (player->MovPos == 0)
15767 player->is_moving = FALSE;
15768 player->is_digging = FALSE;
15769 player->is_collecting = FALSE;
15772 player->is_dropping = FALSE;
15773 player->is_dropping_pressed = FALSE;
15774 player->drop_pressed_delay = 0;
15776 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15779 player->is_snapping = TRUE;
15780 player->is_active = TRUE;
15782 if (player->MovPos == 0)
15784 player->is_moving = FALSE;
15785 player->is_digging = FALSE;
15786 player->is_collecting = FALSE;
15789 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15790 TEST_DrawLevelField(player->last_jx, player->last_jy);
15792 TEST_DrawLevelField(x, y);
15797 static boolean DropElement(struct PlayerInfo *player)
15799 int old_element, new_element;
15800 int dropx = player->jx, dropy = player->jy;
15801 int drop_direction = player->MovDir;
15802 int drop_side = drop_direction;
15804 int drop_element = get_next_dropped_element(player);
15806 int drop_element = (player->inventory_size > 0 ?
15807 player->inventory_element[player->inventory_size - 1] :
15808 player->inventory_infinite_element != EL_UNDEFINED ?
15809 player->inventory_infinite_element :
15810 player->dynabombs_left > 0 ?
15811 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15815 player->is_dropping_pressed = TRUE;
15817 /* do not drop an element on top of another element; when holding drop key
15818 pressed without moving, dropped element must move away before the next
15819 element can be dropped (this is especially important if the next element
15820 is dynamite, which can be placed on background for historical reasons) */
15821 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15824 if (IS_THROWABLE(drop_element))
15826 dropx += GET_DX_FROM_DIR(drop_direction);
15827 dropy += GET_DY_FROM_DIR(drop_direction);
15829 if (!IN_LEV_FIELD(dropx, dropy))
15833 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15834 new_element = drop_element; /* default: no change when dropping */
15836 /* check if player is active, not moving and ready to drop */
15837 if (!player->active || player->MovPos || player->drop_delay > 0)
15840 /* check if player has anything that can be dropped */
15841 if (new_element == EL_UNDEFINED)
15844 /* check if drop key was pressed long enough for EM style dynamite */
15845 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15848 /* check if anything can be dropped at the current position */
15849 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15852 /* collected custom elements can only be dropped on empty fields */
15853 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15856 if (old_element != EL_EMPTY)
15857 Back[dropx][dropy] = old_element; /* store old element on this field */
15859 ResetGfxAnimation(dropx, dropy);
15860 ResetRandomAnimationValue(dropx, dropy);
15862 if (player->inventory_size > 0 ||
15863 player->inventory_infinite_element != EL_UNDEFINED)
15865 if (player->inventory_size > 0)
15867 player->inventory_size--;
15869 DrawGameDoorValues();
15871 if (new_element == EL_DYNAMITE)
15872 new_element = EL_DYNAMITE_ACTIVE;
15873 else if (new_element == EL_EM_DYNAMITE)
15874 new_element = EL_EM_DYNAMITE_ACTIVE;
15875 else if (new_element == EL_SP_DISK_RED)
15876 new_element = EL_SP_DISK_RED_ACTIVE;
15879 Feld[dropx][dropy] = new_element;
15881 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15882 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15883 el2img(Feld[dropx][dropy]), 0);
15885 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15887 /* needed if previous element just changed to "empty" in the last frame */
15888 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15890 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15891 player->index_bit, drop_side);
15892 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15894 player->index_bit, drop_side);
15896 TestIfElementTouchesCustomElement(dropx, dropy);
15898 else /* player is dropping a dyna bomb */
15900 player->dynabombs_left--;
15902 Feld[dropx][dropy] = new_element;
15904 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15905 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15906 el2img(Feld[dropx][dropy]), 0);
15908 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15911 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15912 InitField_WithBug1(dropx, dropy, FALSE);
15914 new_element = Feld[dropx][dropy]; /* element might have changed */
15916 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15917 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15919 int move_direction, nextx, nexty;
15921 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15922 MovDir[dropx][dropy] = drop_direction;
15924 move_direction = MovDir[dropx][dropy];
15925 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15926 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15928 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15930 #if USE_FIX_IMPACT_COLLISION
15931 /* do not cause impact style collision by dropping elements that can fall */
15932 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15934 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15938 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15939 player->is_dropping = TRUE;
15941 player->drop_pressed_delay = 0;
15942 player->is_dropping_pressed = FALSE;
15944 player->drop_x = dropx;
15945 player->drop_y = dropy;
15950 /* ------------------------------------------------------------------------- */
15951 /* game sound playing functions */
15952 /* ------------------------------------------------------------------------- */
15954 static int *loop_sound_frame = NULL;
15955 static int *loop_sound_volume = NULL;
15957 void InitPlayLevelSound()
15959 int num_sounds = getSoundListSize();
15961 checked_free(loop_sound_frame);
15962 checked_free(loop_sound_volume);
15964 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15965 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15968 static void PlayLevelSound(int x, int y, int nr)
15970 int sx = SCREENX(x), sy = SCREENY(y);
15971 int volume, stereo_position;
15972 int max_distance = 8;
15973 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15975 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15976 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15979 if (!IN_LEV_FIELD(x, y) ||
15980 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15981 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15984 volume = SOUND_MAX_VOLUME;
15986 if (!IN_SCR_FIELD(sx, sy))
15988 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15989 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15991 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15994 stereo_position = (SOUND_MAX_LEFT +
15995 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15996 (SCR_FIELDX + 2 * max_distance));
15998 if (IS_LOOP_SOUND(nr))
16000 /* This assures that quieter loop sounds do not overwrite louder ones,
16001 while restarting sound volume comparison with each new game frame. */
16003 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16006 loop_sound_volume[nr] = volume;
16007 loop_sound_frame[nr] = FrameCounter;
16010 PlaySoundExt(nr, volume, stereo_position, type);
16013 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16015 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16016 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16017 y < LEVELY(BY1) ? LEVELY(BY1) :
16018 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16022 static void PlayLevelSoundAction(int x, int y, int action)
16024 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16027 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16029 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16031 if (sound_effect != SND_UNDEFINED)
16032 PlayLevelSound(x, y, sound_effect);
16035 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16038 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16040 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16041 PlayLevelSound(x, y, sound_effect);
16044 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16046 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16048 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16049 PlayLevelSound(x, y, sound_effect);
16052 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16054 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16056 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16057 StopSound(sound_effect);
16060 static void PlayLevelMusic()
16062 if (levelset.music[level_nr] != MUS_UNDEFINED)
16063 PlayMusic(levelset.music[level_nr]); /* from config file */
16065 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16068 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16070 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16071 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16072 int x = xx - 1 - offset;
16073 int y = yy - 1 - offset;
16078 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16082 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16086 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16090 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16094 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16098 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16102 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16105 case SAMPLE_android_clone:
16106 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16109 case SAMPLE_android_move:
16110 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16113 case SAMPLE_spring:
16114 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16118 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16122 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16125 case SAMPLE_eater_eat:
16126 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16130 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16133 case SAMPLE_collect:
16134 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16137 case SAMPLE_diamond:
16138 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16141 case SAMPLE_squash:
16142 /* !!! CHECK THIS !!! */
16144 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16146 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16150 case SAMPLE_wonderfall:
16151 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16155 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16159 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16163 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16167 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16171 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16175 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16178 case SAMPLE_wonder:
16179 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16183 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16186 case SAMPLE_exit_open:
16187 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16190 case SAMPLE_exit_leave:
16191 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16194 case SAMPLE_dynamite:
16195 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16199 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16203 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16207 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16211 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16215 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16219 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16223 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16228 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16230 int element = map_element_SP_to_RND(element_sp);
16231 int action = map_action_SP_to_RND(action_sp);
16232 int offset = (setup.sp_show_border_elements ? 0 : 1);
16233 int x = xx - offset;
16234 int y = yy - offset;
16237 printf("::: %d -> %d\n", element_sp, action_sp);
16240 PlayLevelSoundElementAction(x, y, element, action);
16244 void ChangeTime(int value)
16246 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16250 /* EMC game engine uses value from time counter of RND game engine */
16251 level.native_em_level->lev->time = *time;
16253 DrawGameValue_Time(*time);
16256 void RaiseScore(int value)
16258 /* EMC game engine and RND game engine have separate score counters */
16259 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16260 &level.native_em_level->lev->score : &local_player->score);
16264 DrawGameValue_Score(*score);
16268 void RaiseScore(int value)
16270 local_player->score += value;
16273 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16275 DisplayGameControlValues();
16277 DrawGameValue_Score(local_player->score);
16281 void RaiseScoreElement(int element)
16286 case EL_BD_DIAMOND:
16287 case EL_EMERALD_YELLOW:
16288 case EL_EMERALD_RED:
16289 case EL_EMERALD_PURPLE:
16290 case EL_SP_INFOTRON:
16291 RaiseScore(level.score[SC_EMERALD]);
16294 RaiseScore(level.score[SC_DIAMOND]);
16297 RaiseScore(level.score[SC_CRYSTAL]);
16300 RaiseScore(level.score[SC_PEARL]);
16303 case EL_BD_BUTTERFLY:
16304 case EL_SP_ELECTRON:
16305 RaiseScore(level.score[SC_BUG]);
16308 case EL_BD_FIREFLY:
16309 case EL_SP_SNIKSNAK:
16310 RaiseScore(level.score[SC_SPACESHIP]);
16313 case EL_DARK_YAMYAM:
16314 RaiseScore(level.score[SC_YAMYAM]);
16317 RaiseScore(level.score[SC_ROBOT]);
16320 RaiseScore(level.score[SC_PACMAN]);
16323 RaiseScore(level.score[SC_NUT]);
16326 case EL_EM_DYNAMITE:
16327 case EL_SP_DISK_RED:
16328 case EL_DYNABOMB_INCREASE_NUMBER:
16329 case EL_DYNABOMB_INCREASE_SIZE:
16330 case EL_DYNABOMB_INCREASE_POWER:
16331 RaiseScore(level.score[SC_DYNAMITE]);
16333 case EL_SHIELD_NORMAL:
16334 case EL_SHIELD_DEADLY:
16335 RaiseScore(level.score[SC_SHIELD]);
16337 case EL_EXTRA_TIME:
16338 RaiseScore(level.extra_time_score);
16352 case EL_DC_KEY_WHITE:
16353 RaiseScore(level.score[SC_KEY]);
16356 RaiseScore(element_info[element].collect_score);
16361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16363 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16365 #if defined(NETWORK_AVALIABLE)
16366 if (options.network)
16367 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16376 FadeSkipNextFadeIn();
16378 fading = fading_none;
16382 OpenDoor(DOOR_CLOSE_1);
16385 game_status = GAME_MODE_MAIN;
16388 DrawAndFadeInMainMenu(REDRAW_FIELD);
16396 FadeOut(REDRAW_FIELD);
16399 game_status = GAME_MODE_MAIN;
16401 DrawAndFadeInMainMenu(REDRAW_FIELD);
16405 else /* continue playing the game */
16407 if (tape.playing && tape.deactivate_display)
16408 TapeDeactivateDisplayOff(TRUE);
16410 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16412 if (tape.playing && tape.deactivate_display)
16413 TapeDeactivateDisplayOn();
16417 void RequestQuitGame(boolean ask_if_really_quit)
16419 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16420 boolean skip_request = AllPlayersGone || quick_quit;
16422 RequestQuitGameExt(skip_request, quick_quit,
16423 "Do you really want to quit the game ?");
16427 /* ------------------------------------------------------------------------- */
16428 /* random generator functions */
16429 /* ------------------------------------------------------------------------- */
16431 unsigned int InitEngineRandom_RND(long seed)
16433 game.num_random_calls = 0;
16436 unsigned int rnd_seed = InitEngineRandom(seed);
16438 printf("::: START RND: %d\n", rnd_seed);
16443 return InitEngineRandom(seed);
16449 unsigned int RND(int max)
16453 game.num_random_calls++;
16455 return GetEngineRandom(max);
16462 /* ------------------------------------------------------------------------- */
16463 /* game engine snapshot handling functions */
16464 /* ------------------------------------------------------------------------- */
16466 struct EngineSnapshotInfo
16468 /* runtime values for custom element collect score */
16469 int collect_score[NUM_CUSTOM_ELEMENTS];
16471 /* runtime values for group element choice position */
16472 int choice_pos[NUM_GROUP_ELEMENTS];
16474 /* runtime values for belt position animations */
16475 int belt_graphic[4][NUM_BELT_PARTS];
16476 int belt_anim_mode[4][NUM_BELT_PARTS];
16479 static struct EngineSnapshotInfo engine_snapshot_rnd;
16480 static char *snapshot_level_identifier = NULL;
16481 static int snapshot_level_nr = -1;
16483 static void SaveEngineSnapshotValues_RND()
16485 static int belt_base_active_element[4] =
16487 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16488 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16489 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16490 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16496 int element = EL_CUSTOM_START + i;
16498 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16501 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16503 int element = EL_GROUP_START + i;
16505 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16508 for (i = 0; i < 4; i++)
16510 for (j = 0; j < NUM_BELT_PARTS; j++)
16512 int element = belt_base_active_element[i] + j;
16513 int graphic = el2img(element);
16514 int anim_mode = graphic_info[graphic].anim_mode;
16516 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16517 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16522 static void LoadEngineSnapshotValues_RND()
16524 unsigned long num_random_calls = game.num_random_calls;
16527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16529 int element = EL_CUSTOM_START + i;
16531 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16534 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16536 int element = EL_GROUP_START + i;
16538 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16541 for (i = 0; i < 4; i++)
16543 for (j = 0; j < NUM_BELT_PARTS; j++)
16545 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16546 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16548 graphic_info[graphic].anim_mode = anim_mode;
16552 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16554 InitRND(tape.random_seed);
16555 for (i = 0; i < num_random_calls; i++)
16559 if (game.num_random_calls != num_random_calls)
16561 Error(ERR_INFO, "number of random calls out of sync");
16562 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16563 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16564 Error(ERR_EXIT, "this should not happen -- please debug");
16568 void SaveEngineSnapshot()
16570 /* do not save snapshots from editor */
16571 if (level_editor_test_game)
16574 /* free previous snapshot buffers, if needed */
16575 FreeEngineSnapshotBuffers();
16577 /* copy some special values to a structure better suited for the snapshot */
16579 SaveEngineSnapshotValues_RND();
16580 SaveEngineSnapshotValues_EM();
16581 SaveEngineSnapshotValues_SP();
16583 /* save values stored in special snapshot structure */
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16589 /* save further RND engine values */
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16619 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16622 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16623 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16624 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16625 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16626 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16627 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16628 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16629 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16630 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16631 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16632 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16633 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16634 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16636 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16637 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16639 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16640 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16641 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16643 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16644 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16646 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16649 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16650 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16655 /* save level identification information */
16657 setString(&snapshot_level_identifier, leveldir_current->identifier);
16658 snapshot_level_nr = level_nr;
16661 ListNode *node = engine_snapshot_list_rnd;
16664 while (node != NULL)
16666 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16671 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16675 void LoadEngineSnapshot()
16677 /* restore generically stored snapshot buffers */
16679 LoadEngineSnapshotBuffers();
16681 /* restore special values from snapshot structure */
16683 LoadEngineSnapshotValues_RND();
16684 LoadEngineSnapshotValues_EM();
16685 LoadEngineSnapshotValues_SP();
16688 boolean CheckEngineSnapshot()
16690 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16691 snapshot_level_nr == level_nr);
16695 /* ---------- new game button stuff ---------------------------------------- */
16697 /* graphic position values for game buttons */
16698 #define GAME_BUTTON_XSIZE 30
16699 #define GAME_BUTTON_YSIZE 30
16700 #define GAME_BUTTON_XPOS 5
16701 #define GAME_BUTTON_YPOS 215
16702 #define SOUND_BUTTON_XPOS 5
16703 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16705 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16706 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16707 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16708 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16709 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16710 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16718 } gamebutton_info[NUM_GAME_BUTTONS] =
16722 &game.button.stop.x, &game.button.stop.y,
16723 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16728 &game.button.pause.x, &game.button.pause.y,
16729 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16730 GAME_CTRL_ID_PAUSE,
16734 &game.button.play.x, &game.button.play.y,
16735 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16740 &game.button.sound_music.x, &game.button.sound_music.y,
16741 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16742 SOUND_CTRL_ID_MUSIC,
16743 "background music on/off"
16746 &game.button.sound_loops.x, &game.button.sound_loops.y,
16747 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16748 SOUND_CTRL_ID_LOOPS,
16749 "sound loops on/off"
16752 &game.button.sound_simple.x,&game.button.sound_simple.y,
16753 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16754 SOUND_CTRL_ID_SIMPLE,
16755 "normal sounds on/off"
16759 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16764 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16765 GAME_CTRL_ID_PAUSE,
16769 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16774 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16775 SOUND_CTRL_ID_MUSIC,
16776 "background music on/off"
16779 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16780 SOUND_CTRL_ID_LOOPS,
16781 "sound loops on/off"
16784 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16785 SOUND_CTRL_ID_SIMPLE,
16786 "normal sounds on/off"
16791 void CreateGameButtons()
16795 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16797 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16798 struct GadgetInfo *gi;
16801 unsigned long event_mask;
16803 int gd_xoffset, gd_yoffset;
16804 int gd_x1, gd_x2, gd_y1, gd_y2;
16807 x = DX + *gamebutton_info[i].x;
16808 y = DY + *gamebutton_info[i].y;
16809 gd_xoffset = gamebutton_info[i].gd_x;
16810 gd_yoffset = gamebutton_info[i].gd_y;
16811 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16812 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16814 if (id == GAME_CTRL_ID_STOP ||
16815 id == GAME_CTRL_ID_PAUSE ||
16816 id == GAME_CTRL_ID_PLAY)
16818 button_type = GD_TYPE_NORMAL_BUTTON;
16820 event_mask = GD_EVENT_RELEASED;
16821 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16822 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16826 button_type = GD_TYPE_CHECK_BUTTON;
16828 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16829 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16830 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16831 event_mask = GD_EVENT_PRESSED;
16832 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16833 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16836 gi = CreateGadget(GDI_CUSTOM_ID, id,
16837 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16842 GDI_X, DX + gd_xoffset,
16843 GDI_Y, DY + gd_yoffset,
16845 GDI_WIDTH, GAME_BUTTON_XSIZE,
16846 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16847 GDI_TYPE, button_type,
16848 GDI_STATE, GD_BUTTON_UNPRESSED,
16849 GDI_CHECKED, checked,
16850 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16851 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16852 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16853 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16854 GDI_DIRECT_DRAW, FALSE,
16855 GDI_EVENT_MASK, event_mask,
16856 GDI_CALLBACK_ACTION, HandleGameButtons,
16860 Error(ERR_EXIT, "cannot create gadget");
16862 game_gadget[id] = gi;
16866 void FreeGameButtons()
16870 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16871 FreeGadget(game_gadget[i]);
16874 static void MapGameButtons()
16878 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879 MapGadget(game_gadget[i]);
16882 void UnmapGameButtons()
16886 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887 UnmapGadget(game_gadget[i]);
16890 void RedrawGameButtons()
16894 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895 RedrawGadget(game_gadget[i]);
16898 static void HandleGameButtonsExt(int id)
16900 if (game_status != GAME_MODE_PLAYING)
16905 case GAME_CTRL_ID_STOP:
16909 RequestQuitGame(TRUE);
16912 case GAME_CTRL_ID_PAUSE:
16913 if (options.network)
16915 #if defined(NETWORK_AVALIABLE)
16917 SendToServer_ContinuePlaying();
16919 SendToServer_PausePlaying();
16923 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16926 case GAME_CTRL_ID_PLAY:
16929 #if defined(NETWORK_AVALIABLE)
16930 if (options.network)
16931 SendToServer_ContinuePlaying();
16935 tape.pausing = FALSE;
16936 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16941 case SOUND_CTRL_ID_MUSIC:
16942 if (setup.sound_music)
16944 setup.sound_music = FALSE;
16948 else if (audio.music_available)
16950 setup.sound = setup.sound_music = TRUE;
16952 SetAudioMode(setup.sound);
16958 case SOUND_CTRL_ID_LOOPS:
16959 if (setup.sound_loops)
16960 setup.sound_loops = FALSE;
16961 else if (audio.loops_available)
16963 setup.sound = setup.sound_loops = TRUE;
16965 SetAudioMode(setup.sound);
16969 case SOUND_CTRL_ID_SIMPLE:
16970 if (setup.sound_simple)
16971 setup.sound_simple = FALSE;
16972 else if (audio.sound_available)
16974 setup.sound = setup.sound_simple = TRUE;
16976 SetAudioMode(setup.sound);
16985 static void HandleGameButtons(struct GadgetInfo *gi)
16987 HandleGameButtonsExt(gi->custom_id);
16990 void HandleSoundButtonKeys(Key key)
16993 if (key == setup.shortcut.sound_simple)
16994 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16995 else if (key == setup.shortcut.sound_loops)
16996 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16997 else if (key == setup.shortcut.sound_music)
16998 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17000 if (key == setup.shortcut.sound_simple)
17001 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17002 else if (key == setup.shortcut.sound_loops)
17003 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17004 else if (key == setup.shortcut.sound_music)
17005 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);