1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
277 #define NUM_GAME_PANEL_CONTROLS 114
279 struct GamePanelOrderInfo
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
287 struct GamePanelControlInfo
291 struct TextPosInfo *pos;
294 int value, last_value;
295 int frame, last_frame;
300 static struct GamePanelControlInfo game_panel_controls[] =
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
584 &game.panel.penguins,
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1053 #define NUM_GAME_BUTTONS 6
1056 /* forward declaration for internal use */
1058 static void CreateField(int, int, int);
1060 static void ResetGfxAnimation(int, int);
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1157 if (recursion_loop_detected) \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1166 recursion_loop_depth++; \
1169 #define RECURSION_LOOP_DETECTION_END() \
1171 recursion_loop_depth--; \
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1178 static int map_player_action[MAX_PLAYERS];
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1204 struct ChangingElementInfo
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1214 static struct ChangingElementInfo change_delay_list[] =
1249 EL_STEEL_EXIT_OPENING,
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1293 EL_EM_STEEL_EXIT_CLOSING,
1297 EL_EM_STEEL_EXIT_CLOSED,
1321 EL_SWITCHGATE_OPENING,
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1337 EL_TIMEGATE_OPENING,
1345 EL_TIMEGATE_CLOSING,
1354 EL_ACID_SPLASH_LEFT,
1362 EL_ACID_SPLASH_RIGHT,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1386 EL_SP_BUGGY_BASE_ACTIVE,
1410 EL_ROBOT_WHEEL_ACTIVE,
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1450 EL_DIAGONAL_SHRINKING,
1458 EL_DIAGONAL_GROWING,
1479 int push_delay_fixed, push_delay_random;
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1491 { EL_UNDEFINED, 0, 0 },
1499 move_stepsize_list[] =
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1514 { EL_UNDEFINED, 0 },
1522 collect_count_list[] =
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1530 { EL_SP_INFOTRON, 1 },
1534 { EL_UNDEFINED, 0 },
1542 access_direction_list[] =
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1576 { EL_UNDEFINED, MV_NONE }
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1602 void DEBUG_SetMaximumDynamite()
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1613 static void InitPlayfieldScanModeVars()
1615 if (game.use_reverse_scan_direction)
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1633 static void InitPlayfieldScanMode(int mode)
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1638 InitPlayfieldScanModeVars();
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1649 return TILEX / move_stepsize;
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1670 player->move_delay_value_next = -1;
1672 player->move_delay_reset_counter = 0;
1676 void GetPlayerConfig()
1678 GameFrameDelay = setup.game_frame_delay;
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1694 SetAudioMode(setup.sound);
1698 int GetElementFromGroupElement(int element)
1700 if (IS_GROUP_ELEMENT(element))
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1716 group->choice_pos++;
1718 element = group->element_resolved[element_pos];
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 if (element == EL_SP_MURPHY)
1730 if (stored_player[0].present)
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1745 Feld[x][y] = EL_PLAYER_1;
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1754 player->present = TRUE;
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1760 /* ---------- initialize player's last field block delay --------------- */
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773 if (!options.network || player->connected)
1775 player->active = TRUE;
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1781 StorePlayer[x][y] = Feld[x][y];
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1792 Feld[x][y] = EL_EMPTY;
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1798 #if USE_PLAYER_REANIMATION
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1810 static void InitField(int x, int y, boolean init_game)
1812 int element = Feld[x][y];
1821 InitPlayerField(x, y, element, init_game);
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1891 case EL_AMOEBA_FULL:
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1918 local_player->lights_still_needed++;
1922 local_player->friends_still_needed++;
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1953 else /* more than one switch -- set it like the first switch */
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1972 case EL_LIGHT_SWITCH_ACTIVE:
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2024 if (IS_CUSTOM_ELEMENT(element))
2026 if (CAN_MOVE(element))
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2034 else if (IS_GROUP_ELEMENT(element))
2036 Feld[x][y] = GetElementFromGroupElement(element);
2038 InitField(x, y, init_game);
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2050 InitField(x, y, init_game);
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2060 int old_element = Feld[x][y];
2062 InitField(x, y, init_game);
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2081 static int get_key_element_from_nr(int key_nr)
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2087 return key_base_element + key_nr;
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2145 compare_result = gpo1->nr - gpo2->nr;
2147 return compare_result;
2150 void InitGameControlValues()
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2160 int type = gpc->type;
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2179 else if (type == TYPE_ELEMENT)
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2185 /* fill structure for game panel draw order */
2187 gpo->sort_priority = pos->sort_priority;
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2195 void UpdatePlayfieldElementCount()
2197 boolean use_element_count = FALSE;
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2205 if (!use_element_count)
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2211 SCAN_PLAYFIELD(x, y)
2213 element_info[Feld[x][y]].element_count++;
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2223 void UpdateGameControlValues()
2227 int time = (local_player->LevelSolved ?
2228 local_player->LevelSolved_CountingTime :
2229 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230 level.native_em_level->lev->time :
2231 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232 level.native_sp_level->game_sp->time_played :
2233 game.no_time_limit ? TimePlayed : TimeLeft);
2235 int time = (local_player->LevelSolved ?
2236 local_player->LevelSolved_CountingTime :
2237 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238 level.native_em_level->lev->time :
2239 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240 level.native_sp_level->game_sp->time_played :
2241 level.time == 0 ? TimePlayed : TimeLeft);
2243 int score = (local_player->LevelSolved ?
2244 local_player->LevelSolved_CountingScore :
2245 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->score :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->score :
2249 local_player->score);
2250 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2251 level.native_em_level->lev->required :
2252 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2253 level.native_sp_level->game_sp->infotrons_still_needed :
2254 local_player->gems_still_needed);
2255 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2256 level.native_em_level->lev->required > 0 :
2257 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2258 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2259 local_player->gems_still_needed > 0 ||
2260 local_player->sokobanfields_still_needed > 0 ||
2261 local_player->lights_still_needed > 0);
2263 UpdatePlayfieldElementCount();
2265 /* update game panel control values */
2267 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2268 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2270 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2271 for (i = 0; i < MAX_NUM_KEYS; i++)
2272 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2273 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2274 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2276 if (game.centered_player_nr == -1)
2278 for (i = 0; i < MAX_PLAYERS; i++)
2280 /* only one player in Supaplex game engine */
2281 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2284 for (k = 0; k < MAX_NUM_KEYS; k++)
2286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 if (level.native_em_level->ply[i]->keys & (1 << k))
2289 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290 get_key_element_from_nr(k);
2292 else if (stored_player[i].key[k])
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2297 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299 level.native_em_level->ply[i]->dynamite;
2300 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2301 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302 level.native_sp_level->game_sp->red_disk_count;
2304 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2305 stored_player[i].inventory_size;
2307 if (stored_player[i].num_white_keys > 0)
2308 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2311 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312 stored_player[i].num_white_keys;
2317 int player_nr = game.centered_player_nr;
2319 for (k = 0; k < MAX_NUM_KEYS; k++)
2321 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2324 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2325 get_key_element_from_nr(k);
2327 else if (stored_player[player_nr].key[k])
2328 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2329 get_key_element_from_nr(k);
2332 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2333 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2334 level.native_em_level->ply[player_nr]->dynamite;
2335 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2336 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2337 level.native_sp_level->game_sp->red_disk_count;
2339 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2340 stored_player[player_nr].inventory_size;
2342 if (stored_player[player_nr].num_white_keys > 0)
2343 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2345 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2346 stored_player[player_nr].num_white_keys;
2349 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2351 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2352 get_inventory_element_from_pos(local_player, i);
2353 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2354 get_inventory_element_from_pos(local_player, -i - 1);
2357 game_panel_controls[GAME_PANEL_SCORE].value = score;
2358 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2360 game_panel_controls[GAME_PANEL_TIME].value = time;
2362 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2363 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2364 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2366 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2368 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2369 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2371 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2372 local_player->shield_normal_time_left;
2373 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2374 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2376 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2377 local_player->shield_deadly_time_left;
2379 game_panel_controls[GAME_PANEL_EXIT].value =
2380 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2382 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2383 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2384 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2385 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2386 EL_EMC_MAGIC_BALL_SWITCH);
2388 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2389 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2390 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2391 game.light_time_left;
2393 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2394 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2395 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2396 game.timegate_time_left;
2398 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2399 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2401 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2402 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2403 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2404 game.lenses_time_left;
2406 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2407 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2408 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2409 game.magnify_time_left;
2411 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2412 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2413 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2414 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2415 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2416 EL_BALLOON_SWITCH_NONE);
2418 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2419 local_player->dynabomb_count;
2420 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2421 local_player->dynabomb_size;
2422 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2423 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2425 game_panel_controls[GAME_PANEL_PENGUINS].value =
2426 local_player->friends_still_needed;
2428 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2429 local_player->sokobanfields_still_needed;
2430 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2431 local_player->sokobanfields_still_needed;
2433 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2434 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2436 for (i = 0; i < NUM_BELTS; i++)
2438 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2439 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2440 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2441 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2442 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2445 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2446 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2447 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2448 game.magic_wall_time_left;
2450 #if USE_PLAYER_GRAVITY
2451 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2452 local_player->gravity;
2454 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2457 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2458 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2460 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2461 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2462 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2463 game.panel.element[i].id : EL_UNDEFINED);
2465 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2466 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2467 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2468 element_info[game.panel.element_count[i].id].element_count : 0);
2470 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2471 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2472 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2473 element_info[game.panel.ce_score[i].id].collect_score : 0);
2475 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2476 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2477 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2478 element_info[game.panel.ce_score_element[i].id].collect_score :
2481 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2482 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2483 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2485 /* update game panel control frames */
2487 for (i = 0; game_panel_controls[i].nr != -1; i++)
2489 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2491 if (gpc->type == TYPE_ELEMENT)
2493 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2495 int last_anim_random_frame = gfx.anim_random_frame;
2496 int element = gpc->value;
2497 int graphic = el2panelimg(element);
2499 if (gpc->value != gpc->last_value)
2502 gpc->gfx_random = INIT_GFX_RANDOM();
2508 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2509 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2510 gpc->gfx_random = INIT_GFX_RANDOM();
2513 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2514 gfx.anim_random_frame = gpc->gfx_random;
2516 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2517 gpc->gfx_frame = element_info[element].collect_score;
2519 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2522 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2523 gfx.anim_random_frame = last_anim_random_frame;
2529 void DisplayGameControlValues()
2531 boolean redraw_panel = FALSE;
2534 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538 if (PANEL_DEACTIVATED(gpc->pos))
2541 if (gpc->value == gpc->last_value &&
2542 gpc->frame == gpc->last_frame)
2545 redraw_panel = TRUE;
2551 /* copy default game door content to main double buffer */
2553 /* !!! CHECK AGAIN !!! */
2554 SetPanelBackground();
2555 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2556 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2558 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2559 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2562 /* redraw game control buttons */
2564 RedrawGameButtons();
2570 game_status = GAME_MODE_PSEUDO_PANEL;
2573 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2575 for (i = 0; game_panel_controls[i].nr != -1; i++)
2579 int nr = game_panel_order[i].nr;
2580 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2582 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2585 struct TextPosInfo *pos = gpc->pos;
2586 int type = gpc->type;
2587 int value = gpc->value;
2588 int frame = gpc->frame;
2590 int last_value = gpc->last_value;
2591 int last_frame = gpc->last_frame;
2593 int size = pos->size;
2594 int font = pos->font;
2595 boolean draw_masked = pos->draw_masked;
2596 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2598 if (PANEL_DEACTIVATED(pos))
2602 if (value == last_value && frame == last_frame)
2606 gpc->last_value = value;
2607 gpc->last_frame = frame;
2610 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2613 if (type == TYPE_INTEGER)
2615 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2616 nr == GAME_PANEL_TIME)
2618 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2620 if (use_dynamic_size) /* use dynamic number of digits */
2622 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2623 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2624 int size2 = size1 + 1;
2625 int font1 = pos->font;
2626 int font2 = pos->font_alt;
2628 size = (value < value_change ? size1 : size2);
2629 font = (value < value_change ? font1 : font2);
2632 /* clear background if value just changed its size (dynamic digits) */
2633 if ((last_value < value_change) != (value < value_change))
2635 int width1 = size1 * getFontWidth(font1);
2636 int width2 = size2 * getFontWidth(font2);
2637 int max_width = MAX(width1, width2);
2638 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2640 pos->width = max_width;
2642 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2643 max_width, max_height);
2650 /* correct text size if "digits" is zero or less */
2652 size = strlen(int2str(value, size));
2654 /* dynamically correct text alignment */
2655 pos->width = size * getFontWidth(font);
2658 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2659 int2str(value, size), font, mask_mode);
2661 else if (type == TYPE_ELEMENT)
2663 int element, graphic;
2667 int dst_x = PANEL_XPOS(pos);
2668 int dst_y = PANEL_YPOS(pos);
2671 if (value != EL_UNDEFINED && value != EL_EMPTY)
2674 graphic = el2panelimg(value);
2676 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2679 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2683 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2686 width = graphic_info[graphic].width * size / TILESIZE;
2687 height = graphic_info[graphic].height * size / TILESIZE;
2691 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2692 dst_x - src_x, dst_y - src_y);
2693 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2698 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2703 if (value == EL_UNDEFINED || value == EL_EMPTY)
2705 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2706 graphic = el2panelimg(element);
2708 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2709 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2710 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2715 graphic = el2panelimg(value);
2717 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2720 width = graphic_info[graphic].width * size / TILESIZE;
2721 height = graphic_info[graphic].height * size / TILESIZE;
2723 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2726 else if (type == TYPE_STRING)
2728 boolean active = (value != 0);
2729 char *state_normal = "off";
2730 char *state_active = "on";
2731 char *state = (active ? state_active : state_normal);
2732 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2733 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2734 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2735 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2737 if (nr == GAME_PANEL_GRAVITY_STATE)
2739 int font1 = pos->font; /* (used for normal state) */
2740 int font2 = pos->font_alt; /* (used for active state) */
2742 int size1 = strlen(state_normal);
2743 int size2 = strlen(state_active);
2744 int width1 = size1 * getFontWidth(font1);
2745 int width2 = size2 * getFontWidth(font2);
2746 int max_width = MAX(width1, width2);
2747 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2749 pos->width = max_width;
2751 /* clear background for values that may have changed its size */
2752 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2753 max_width, max_height);
2756 font = (active ? font2 : font1);
2766 /* don't truncate output if "chars" is zero or less */
2769 /* dynamically correct text alignment */
2770 pos->width = size * getFontWidth(font);
2774 s_cut = getStringCopyN(s, size);
2776 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2777 s_cut, font, mask_mode);
2783 redraw_mask |= REDRAW_DOOR_1;
2786 game_status = GAME_MODE_PLAYING;
2789 void UpdateAndDisplayGameControlValues()
2791 if (tape.warp_forward)
2794 UpdateGameControlValues();
2795 DisplayGameControlValues();
2798 void DrawGameValue_Emeralds(int value)
2800 struct TextPosInfo *pos = &game.panel.gems;
2802 int font_nr = pos->font;
2804 int font_nr = FONT_TEXT_2;
2806 int font_width = getFontWidth(font_nr);
2807 int chars = pos->size;
2810 return; /* !!! USE NEW STUFF !!! */
2813 if (PANEL_DEACTIVATED(pos))
2816 pos->width = chars * font_width;
2818 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2821 void DrawGameValue_Dynamite(int value)
2823 struct TextPosInfo *pos = &game.panel.inventory_count;
2825 int font_nr = pos->font;
2827 int font_nr = FONT_TEXT_2;
2829 int font_width = getFontWidth(font_nr);
2830 int chars = pos->size;
2833 return; /* !!! USE NEW STUFF !!! */
2836 if (PANEL_DEACTIVATED(pos))
2839 pos->width = chars * font_width;
2841 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2844 void DrawGameValue_Score(int value)
2846 struct TextPosInfo *pos = &game.panel.score;
2848 int font_nr = pos->font;
2850 int font_nr = FONT_TEXT_2;
2852 int font_width = getFontWidth(font_nr);
2853 int chars = pos->size;
2856 return; /* !!! USE NEW STUFF !!! */
2859 if (PANEL_DEACTIVATED(pos))
2862 pos->width = chars * font_width;
2864 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2867 void DrawGameValue_Time(int value)
2869 struct TextPosInfo *pos = &game.panel.time;
2870 static int last_value = -1;
2873 int chars = pos->size;
2875 int font1_nr = pos->font;
2876 int font2_nr = pos->font_alt;
2878 int font1_nr = FONT_TEXT_2;
2879 int font2_nr = FONT_TEXT_1;
2881 int font_nr = font1_nr;
2882 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2885 return; /* !!! USE NEW STUFF !!! */
2888 if (PANEL_DEACTIVATED(pos))
2891 if (use_dynamic_chars) /* use dynamic number of chars */
2893 chars = (value < 1000 ? chars1 : chars2);
2894 font_nr = (value < 1000 ? font1_nr : font2_nr);
2897 /* clear background if value just changed its size (dynamic chars only) */
2898 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2900 int width1 = chars1 * getFontWidth(font1_nr);
2901 int width2 = chars2 * getFontWidth(font2_nr);
2902 int max_width = MAX(width1, width2);
2903 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2905 pos->width = max_width;
2907 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2908 max_width, max_height);
2911 pos->width = chars * getFontWidth(font_nr);
2913 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2918 void DrawGameValue_Level(int value)
2920 struct TextPosInfo *pos = &game.panel.level_number;
2923 int chars = pos->size;
2925 int font1_nr = pos->font;
2926 int font2_nr = pos->font_alt;
2928 int font1_nr = FONT_TEXT_2;
2929 int font2_nr = FONT_TEXT_1;
2931 int font_nr = font1_nr;
2932 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2935 return; /* !!! USE NEW STUFF !!! */
2938 if (PANEL_DEACTIVATED(pos))
2941 if (use_dynamic_chars) /* use dynamic number of chars */
2943 chars = (level_nr < 100 ? chars1 : chars2);
2944 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2947 pos->width = chars * getFontWidth(font_nr);
2949 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2952 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2955 struct TextPosInfo *pos = &game.panel.keys;
2958 int base_key_graphic = EL_KEY_1;
2963 return; /* !!! USE NEW STUFF !!! */
2967 if (PANEL_DEACTIVATED(pos))
2972 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2973 base_key_graphic = EL_EM_KEY_1;
2977 pos->width = 4 * MINI_TILEX;
2981 for (i = 0; i < MAX_NUM_KEYS; i++)
2983 /* currently only 4 of 8 possible keys are displayed */
2984 for (i = 0; i < STD_NUM_KEYS; i++)
2988 struct TextPosInfo *pos = &game.panel.key[i];
2990 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2991 int src_y = DOOR_GFX_PAGEY1 + 123;
2993 int dst_x = PANEL_XPOS(pos);
2994 int dst_y = PANEL_YPOS(pos);
2996 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2997 int dst_y = PANEL_YPOS(pos);
3001 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
3002 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
3004 int graphic = el2edimg(element);
3008 if (PANEL_DEACTIVATED(pos))
3013 /* masked blit with tiles from half-size scaled bitmap does not work yet
3014 (no mask bitmap created for these sizes after loading and scaling) --
3015 solution: load without creating mask, scale, then create final mask */
3017 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3018 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3023 int graphic = el2edimg(base_key_graphic + i);
3028 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3030 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3031 dst_x - src_x, dst_y - src_y);
3032 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3038 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3040 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3041 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3044 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3046 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3047 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3055 void DrawGameValue_Emeralds(int value)
3057 int font_nr = FONT_TEXT_2;
3058 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3060 if (PANEL_DEACTIVATED(game.panel.gems))
3063 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3066 void DrawGameValue_Dynamite(int value)
3068 int font_nr = FONT_TEXT_2;
3069 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3071 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3074 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3077 void DrawGameValue_Score(int value)
3079 int font_nr = FONT_TEXT_2;
3080 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3082 if (PANEL_DEACTIVATED(game.panel.score))
3085 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3088 void DrawGameValue_Time(int value)
3090 int font1_nr = FONT_TEXT_2;
3092 int font2_nr = FONT_TEXT_1;
3094 int font2_nr = FONT_LEVEL_NUMBER;
3096 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3097 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3099 if (PANEL_DEACTIVATED(game.panel.time))
3102 /* clear background if value just changed its size */
3103 if (value == 999 || value == 1000)
3104 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3107 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3109 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3112 void DrawGameValue_Level(int value)
3114 int font1_nr = FONT_TEXT_2;
3116 int font2_nr = FONT_TEXT_1;
3118 int font2_nr = FONT_LEVEL_NUMBER;
3121 if (PANEL_DEACTIVATED(game.panel.level))
3125 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3127 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3130 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3132 int base_key_graphic = EL_KEY_1;
3135 if (PANEL_DEACTIVATED(game.panel.keys))
3138 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3139 base_key_graphic = EL_EM_KEY_1;
3141 /* currently only 4 of 8 possible keys are displayed */
3142 for (i = 0; i < STD_NUM_KEYS; i++)
3144 int x = XX_KEYS + i * MINI_TILEX;
3148 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3150 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3151 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3157 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3160 int key[MAX_NUM_KEYS];
3163 /* prevent EM engine from updating time/score values parallel to GameWon() */
3164 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3165 local_player->LevelSolved)
3168 for (i = 0; i < MAX_NUM_KEYS; i++)
3169 key[i] = key_bits & (1 << i);
3171 DrawGameValue_Level(level_nr);
3173 DrawGameValue_Emeralds(emeralds);
3174 DrawGameValue_Dynamite(dynamite);
3175 DrawGameValue_Score(score);
3176 DrawGameValue_Time(time);
3178 DrawGameValue_Keys(key);
3181 void UpdateGameDoorValues()
3183 UpdateGameControlValues();
3186 void DrawGameDoorValues()
3188 DisplayGameControlValues();
3191 void DrawGameDoorValues_OLD()
3193 int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
3194 int dynamite_value = 0;
3195 int score_value = (local_player->LevelSolved ? local_player->score_final :
3196 local_player->score);
3197 int gems_value = local_player->gems_still_needed;
3201 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3203 DrawGameDoorValues_EM();
3208 if (game.centered_player_nr == -1)
3210 for (i = 0; i < MAX_PLAYERS; i++)
3212 for (j = 0; j < MAX_NUM_KEYS; j++)
3213 if (stored_player[i].key[j])
3214 key_bits |= (1 << j);
3216 dynamite_value += stored_player[i].inventory_size;
3221 int player_nr = game.centered_player_nr;
3223 for (i = 0; i < MAX_NUM_KEYS; i++)
3224 if (stored_player[player_nr].key[i])
3225 key_bits |= (1 << i);
3227 dynamite_value = stored_player[player_nr].inventory_size;
3230 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3236 =============================================================================
3238 -----------------------------------------------------------------------------
3239 initialize game engine due to level / tape version number
3240 =============================================================================
3243 static void InitGameEngine()
3245 int i, j, k, l, x, y;
3247 /* set game engine from tape file when re-playing, else from level file */
3248 game.engine_version = (tape.playing ? tape.engine_version :
3249 level.game_version);
3251 /* ---------------------------------------------------------------------- */
3252 /* set flags for bugs and changes according to active game engine version */
3253 /* ---------------------------------------------------------------------- */
3256 Summary of bugfix/change:
3257 Fixed handling for custom elements that change when pushed by the player.
3259 Fixed/changed in version:
3263 Before 3.1.0, custom elements that "change when pushing" changed directly
3264 after the player started pushing them (until then handled in "DigField()").
3265 Since 3.1.0, these custom elements are not changed until the "pushing"
3266 move of the element is finished (now handled in "ContinueMoving()").
3268 Affected levels/tapes:
3269 The first condition is generally needed for all levels/tapes before version
3270 3.1.0, which might use the old behaviour before it was changed; known tapes
3271 that are affected are some tapes from the level set "Walpurgis Gardens" by
3273 The second condition is an exception from the above case and is needed for
3274 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3275 above (including some development versions of 3.1.0), but before it was
3276 known that this change would break tapes like the above and was fixed in
3277 3.1.1, so that the changed behaviour was active although the engine version
3278 while recording maybe was before 3.1.0. There is at least one tape that is
3279 affected by this exception, which is the tape for the one-level set "Bug
3280 Machine" by Juergen Bonhagen.
3283 game.use_change_when_pushing_bug =
3284 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3286 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3287 tape.game_version < VERSION_IDENT(3,1,1,0)));
3290 Summary of bugfix/change:
3291 Fixed handling for blocking the field the player leaves when moving.
3293 Fixed/changed in version:
3297 Before 3.1.1, when "block last field when moving" was enabled, the field
3298 the player is leaving when moving was blocked for the time of the move,
3299 and was directly unblocked afterwards. This resulted in the last field
3300 being blocked for exactly one less than the number of frames of one player
3301 move. Additionally, even when blocking was disabled, the last field was
3302 blocked for exactly one frame.
3303 Since 3.1.1, due to changes in player movement handling, the last field
3304 is not blocked at all when blocking is disabled. When blocking is enabled,
3305 the last field is blocked for exactly the number of frames of one player
3306 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3307 last field is blocked for exactly one more than the number of frames of
3310 Affected levels/tapes:
3311 (!!! yet to be determined -- probably many !!!)
3314 game.use_block_last_field_bug =
3315 (game.engine_version < VERSION_IDENT(3,1,1,0));
3318 Summary of bugfix/change:
3319 Changed behaviour of CE changes with multiple changes per single frame.
3321 Fixed/changed in version:
3325 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3326 This resulted in race conditions where CEs seem to behave strange in some
3327 situations (where triggered CE changes were just skipped because there was
3328 already a CE change on that tile in the playfield in that engine frame).
3329 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3330 (The number of changes per frame must be limited in any case, because else
3331 it is easily possible to define CE changes that would result in an infinite
3332 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3333 should be set large enough so that it would only be reached in cases where
3334 the corresponding CE change conditions run into a loop. Therefore, it seems
3335 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3336 maximal number of change pages for custom elements.)
3338 Affected levels/tapes:
3342 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3343 game.max_num_changes_per_frame = 1;
3345 game.max_num_changes_per_frame =
3346 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3349 /* ---------------------------------------------------------------------- */
3351 /* default scan direction: scan playfield from top/left to bottom/right */
3352 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3354 /* dynamically adjust element properties according to game engine version */
3355 InitElementPropertiesEngine(game.engine_version);
3358 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3359 printf(" tape version == %06d [%s] [file: %06d]\n",
3360 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3362 printf(" => game.engine_version == %06d\n", game.engine_version);
3365 /* ---------- initialize player's initial move delay --------------------- */
3367 /* dynamically adjust player properties according to level information */
3368 for (i = 0; i < MAX_PLAYERS; i++)
3369 game.initial_move_delay_value[i] =
3370 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3372 /* dynamically adjust player properties according to game engine version */
3373 for (i = 0; i < MAX_PLAYERS; i++)
3374 game.initial_move_delay[i] =
3375 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3376 game.initial_move_delay_value[i] : 0);
3378 /* ---------- initialize player's initial push delay --------------------- */
3380 /* dynamically adjust player properties according to game engine version */
3381 game.initial_push_delay_value =
3382 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3384 /* ---------- initialize changing elements ------------------------------- */
3386 /* initialize changing elements information */
3387 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3389 struct ElementInfo *ei = &element_info[i];
3391 /* this pointer might have been changed in the level editor */
3392 ei->change = &ei->change_page[0];
3394 if (!IS_CUSTOM_ELEMENT(i))
3396 ei->change->target_element = EL_EMPTY_SPACE;
3397 ei->change->delay_fixed = 0;
3398 ei->change->delay_random = 0;
3399 ei->change->delay_frames = 1;
3402 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3404 ei->has_change_event[j] = FALSE;
3406 ei->event_page_nr[j] = 0;
3407 ei->event_page[j] = &ei->change_page[0];
3411 /* add changing elements from pre-defined list */
3412 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3414 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3415 struct ElementInfo *ei = &element_info[ch_delay->element];
3417 ei->change->target_element = ch_delay->target_element;
3418 ei->change->delay_fixed = ch_delay->change_delay;
3420 ei->change->pre_change_function = ch_delay->pre_change_function;
3421 ei->change->change_function = ch_delay->change_function;
3422 ei->change->post_change_function = ch_delay->post_change_function;
3424 ei->change->can_change = TRUE;
3425 ei->change->can_change_or_has_action = TRUE;
3427 ei->has_change_event[CE_DELAY] = TRUE;
3429 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3430 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3433 /* ---------- initialize internal run-time variables --------------------- */
3435 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3437 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3439 for (j = 0; j < ei->num_change_pages; j++)
3441 ei->change_page[j].can_change_or_has_action =
3442 (ei->change_page[j].can_change |
3443 ei->change_page[j].has_action);
3447 /* add change events from custom element configuration */
3448 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3450 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3452 for (j = 0; j < ei->num_change_pages; j++)
3454 if (!ei->change_page[j].can_change_or_has_action)
3457 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3459 /* only add event page for the first page found with this event */
3460 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3462 ei->has_change_event[k] = TRUE;
3464 ei->event_page_nr[k] = j;
3465 ei->event_page[k] = &ei->change_page[j];
3472 /* ---------- initialize reference elements in change conditions --------- */
3474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3476 int element = EL_CUSTOM_START + i;
3477 struct ElementInfo *ei = &element_info[element];
3479 for (j = 0; j < ei->num_change_pages; j++)
3481 int trigger_element = ei->change_page[j].initial_trigger_element;
3483 if (trigger_element >= EL_PREV_CE_8 &&
3484 trigger_element <= EL_NEXT_CE_8)
3485 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3487 ei->change_page[j].trigger_element = trigger_element;
3492 /* ---------- initialize run-time trigger player and element ------------- */
3494 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3496 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3498 for (j = 0; j < ei->num_change_pages; j++)
3500 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3501 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3502 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3503 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3504 ei->change_page[j].actual_trigger_ce_value = 0;
3505 ei->change_page[j].actual_trigger_ce_score = 0;
3509 /* ---------- initialize trigger events ---------------------------------- */
3511 /* initialize trigger events information */
3512 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3513 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3514 trigger_events[i][j] = FALSE;
3516 /* add trigger events from element change event properties */
3517 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3519 struct ElementInfo *ei = &element_info[i];
3521 for (j = 0; j < ei->num_change_pages; j++)
3523 if (!ei->change_page[j].can_change_or_has_action)
3526 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3528 int trigger_element = ei->change_page[j].trigger_element;
3530 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3532 if (ei->change_page[j].has_event[k])
3534 if (IS_GROUP_ELEMENT(trigger_element))
3536 struct ElementGroupInfo *group =
3537 element_info[trigger_element].group;
3539 for (l = 0; l < group->num_elements_resolved; l++)
3540 trigger_events[group->element_resolved[l]][k] = TRUE;
3542 else if (trigger_element == EL_ANY_ELEMENT)
3543 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3544 trigger_events[l][k] = TRUE;
3546 trigger_events[trigger_element][k] = TRUE;
3553 /* ---------- initialize push delay -------------------------------------- */
3555 /* initialize push delay values to default */
3556 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558 if (!IS_CUSTOM_ELEMENT(i))
3560 /* set default push delay values (corrected since version 3.0.7-1) */
3561 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3563 element_info[i].push_delay_fixed = 2;
3564 element_info[i].push_delay_random = 8;
3568 element_info[i].push_delay_fixed = 8;
3569 element_info[i].push_delay_random = 8;
3574 /* set push delay value for certain elements from pre-defined list */
3575 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3577 int e = push_delay_list[i].element;
3579 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3580 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3583 /* set push delay value for Supaplex elements for newer engine versions */
3584 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3586 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3588 if (IS_SP_ELEMENT(i))
3590 /* set SP push delay to just enough to push under a falling zonk */
3591 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3593 element_info[i].push_delay_fixed = delay;
3594 element_info[i].push_delay_random = 0;
3599 /* ---------- initialize move stepsize ----------------------------------- */
3601 /* initialize move stepsize values to default */
3602 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3603 if (!IS_CUSTOM_ELEMENT(i))
3604 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3606 /* set move stepsize value for certain elements from pre-defined list */
3607 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3609 int e = move_stepsize_list[i].element;
3611 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3614 /* ---------- initialize collect score ----------------------------------- */
3616 /* initialize collect score values for custom elements from initial value */
3617 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3618 if (IS_CUSTOM_ELEMENT(i))
3619 element_info[i].collect_score = element_info[i].collect_score_initial;
3621 /* ---------- initialize collect count ----------------------------------- */
3623 /* initialize collect count values for non-custom elements */
3624 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3625 if (!IS_CUSTOM_ELEMENT(i))
3626 element_info[i].collect_count_initial = 0;
3628 /* add collect count values for all elements from pre-defined list */
3629 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3630 element_info[collect_count_list[i].element].collect_count_initial =
3631 collect_count_list[i].count;
3633 /* ---------- initialize access direction -------------------------------- */
3635 /* initialize access direction values to default (access from every side) */
3636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3637 if (!IS_CUSTOM_ELEMENT(i))
3638 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3640 /* set access direction value for certain elements from pre-defined list */
3641 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3642 element_info[access_direction_list[i].element].access_direction =
3643 access_direction_list[i].direction;
3645 /* ---------- initialize explosion content ------------------------------- */
3646 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3648 if (IS_CUSTOM_ELEMENT(i))
3651 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3653 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3655 element_info[i].content.e[x][y] =
3656 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3657 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3658 i == EL_PLAYER_3 ? EL_EMERALD :
3659 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3660 i == EL_MOLE ? EL_EMERALD_RED :
3661 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3662 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3663 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3664 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3665 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3666 i == EL_WALL_EMERALD ? EL_EMERALD :
3667 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3668 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3669 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3670 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3671 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3672 i == EL_WALL_PEARL ? EL_PEARL :
3673 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3678 /* ---------- initialize recursion detection ------------------------------ */
3679 recursion_loop_depth = 0;
3680 recursion_loop_detected = FALSE;
3681 recursion_loop_element = EL_UNDEFINED;
3683 /* ---------- initialize graphics engine ---------------------------------- */
3684 game.scroll_delay_value =
3685 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3686 setup.scroll_delay ? setup.scroll_delay_value : 0);
3687 game.scroll_delay_value =
3688 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3691 int get_num_special_action(int element, int action_first, int action_last)
3693 int num_special_action = 0;
3696 for (i = action_first; i <= action_last; i++)
3698 boolean found = FALSE;
3700 for (j = 0; j < NUM_DIRECTIONS; j++)
3701 if (el_act_dir2img(element, i, j) !=
3702 el_act_dir2img(element, ACTION_DEFAULT, j))
3706 num_special_action++;
3711 return num_special_action;
3716 =============================================================================
3718 -----------------------------------------------------------------------------
3719 initialize and start new game
3720 =============================================================================
3725 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3726 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3727 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3729 boolean do_fading = (game_status == GAME_MODE_MAIN);
3732 int initial_move_dir = MV_DOWN;
3734 int initial_move_dir = MV_NONE;
3738 game_status = GAME_MODE_PLAYING;
3741 /* needed if different viewport properties defined for playing */
3742 ChangeViewportPropertiesIfNeeded();
3746 DrawCompleteVideoDisplay();
3750 InitGameControlValues();
3752 /* don't play tapes over network */
3753 network_playing = (options.network && !tape.playing);
3755 for (i = 0; i < MAX_PLAYERS; i++)
3757 struct PlayerInfo *player = &stored_player[i];
3759 player->index_nr = i;
3760 player->index_bit = (1 << i);
3761 player->element_nr = EL_PLAYER_1 + i;
3763 player->present = FALSE;
3764 player->active = FALSE;
3765 player->mapped = FALSE;
3767 player->killed = FALSE;
3768 player->reanimated = FALSE;
3771 player->effective_action = 0;
3772 player->programmed_action = 0;
3775 player->score_final = 0;
3777 player->gems_still_needed = level.gems_needed;
3778 player->sokobanfields_still_needed = 0;
3779 player->lights_still_needed = 0;
3780 player->friends_still_needed = 0;
3782 for (j = 0; j < MAX_NUM_KEYS; j++)
3783 player->key[j] = FALSE;
3785 player->num_white_keys = 0;
3787 player->dynabomb_count = 0;
3788 player->dynabomb_size = 1;
3789 player->dynabombs_left = 0;
3790 player->dynabomb_xl = FALSE;
3792 player->MovDir = initial_move_dir;
3795 player->GfxDir = initial_move_dir;
3796 player->GfxAction = ACTION_DEFAULT;
3798 player->StepFrame = 0;
3800 player->initial_element = player->element_nr;
3801 player->artwork_element =
3802 (level.use_artwork_element[i] ? level.artwork_element[i] :
3803 player->element_nr);
3804 player->use_murphy = FALSE;
3806 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3807 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3809 player->gravity = level.initial_player_gravity[i];
3811 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3813 player->actual_frame_counter = 0;
3815 player->step_counter = 0;
3817 player->last_move_dir = initial_move_dir;
3819 player->is_active = FALSE;
3821 player->is_waiting = FALSE;
3822 player->is_moving = FALSE;
3823 player->is_auto_moving = FALSE;
3824 player->is_digging = FALSE;
3825 player->is_snapping = FALSE;
3826 player->is_collecting = FALSE;
3827 player->is_pushing = FALSE;
3828 player->is_switching = FALSE;
3829 player->is_dropping = FALSE;
3830 player->is_dropping_pressed = FALSE;
3832 player->is_bored = FALSE;
3833 player->is_sleeping = FALSE;
3835 player->frame_counter_bored = -1;
3836 player->frame_counter_sleeping = -1;
3838 player->anim_delay_counter = 0;
3839 player->post_delay_counter = 0;
3841 player->dir_waiting = initial_move_dir;
3842 player->action_waiting = ACTION_DEFAULT;
3843 player->last_action_waiting = ACTION_DEFAULT;
3844 player->special_action_bored = ACTION_DEFAULT;
3845 player->special_action_sleeping = ACTION_DEFAULT;
3847 player->switch_x = -1;
3848 player->switch_y = -1;
3850 player->drop_x = -1;
3851 player->drop_y = -1;
3853 player->show_envelope = 0;
3855 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3857 player->push_delay = -1; /* initialized when pushing starts */
3858 player->push_delay_value = game.initial_push_delay_value;
3860 player->drop_delay = 0;
3861 player->drop_pressed_delay = 0;
3863 player->last_jx = -1;
3864 player->last_jy = -1;
3868 player->shield_normal_time_left = 0;
3869 player->shield_deadly_time_left = 0;
3871 player->inventory_infinite_element = EL_UNDEFINED;
3872 player->inventory_size = 0;
3874 if (level.use_initial_inventory[i])
3876 for (j = 0; j < level.initial_inventory_size[i]; j++)
3878 int element = level.initial_inventory_content[i][j];
3879 int collect_count = element_info[element].collect_count_initial;
3882 if (!IS_CUSTOM_ELEMENT(element))
3885 if (collect_count == 0)
3886 player->inventory_infinite_element = element;
3888 for (k = 0; k < collect_count; k++)
3889 if (player->inventory_size < MAX_INVENTORY_SIZE)
3890 player->inventory_element[player->inventory_size++] = element;
3894 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3895 SnapField(player, 0, 0);
3897 player->LevelSolved = FALSE;
3898 player->GameOver = FALSE;
3900 player->LevelSolved_GameWon = FALSE;
3901 player->LevelSolved_GameEnd = FALSE;
3902 player->LevelSolved_PanelOff = FALSE;
3903 player->LevelSolved_SaveTape = FALSE;
3904 player->LevelSolved_SaveScore = FALSE;
3905 player->LevelSolved_CountingTime = 0;
3906 player->LevelSolved_CountingScore = 0;
3908 map_player_action[i] = i;
3911 network_player_action_received = FALSE;
3913 #if defined(NETWORK_AVALIABLE)
3914 /* initial null action */
3915 if (network_playing)
3916 SendToServer_MovePlayer(MV_NONE);
3925 TimeLeft = level.time;
3928 ScreenMovDir = MV_NONE;
3932 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3934 AllPlayersGone = FALSE;
3936 game.no_time_limit = (level.time == 0);
3938 game.yamyam_content_nr = 0;
3939 game.robot_wheel_active = FALSE;
3940 game.magic_wall_active = FALSE;
3941 game.magic_wall_time_left = 0;
3942 game.light_time_left = 0;
3943 game.timegate_time_left = 0;
3944 game.switchgate_pos = 0;
3945 game.wind_direction = level.wind_direction_initial;
3947 #if !USE_PLAYER_GRAVITY
3948 game.gravity = FALSE;
3949 game.explosions_delayed = TRUE;
3952 game.lenses_time_left = 0;
3953 game.magnify_time_left = 0;
3955 game.ball_state = level.ball_state_initial;
3956 game.ball_content_nr = 0;
3958 game.envelope_active = FALSE;
3960 /* set focus to local player for network games, else to all players */
3961 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3962 game.centered_player_nr_next = game.centered_player_nr;
3963 game.set_centered_player = FALSE;
3965 if (network_playing && tape.recording)
3967 /* store client dependent player focus when recording network games */
3968 tape.centered_player_nr_next = game.centered_player_nr_next;
3969 tape.set_centered_player = TRUE;
3972 for (i = 0; i < NUM_BELTS; i++)
3974 game.belt_dir[i] = MV_NONE;
3975 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3978 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3979 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3981 SCAN_PLAYFIELD(x, y)
3983 Feld[x][y] = level.field[x][y];
3984 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3985 ChangeDelay[x][y] = 0;
3986 ChangePage[x][y] = -1;
3987 #if USE_NEW_CUSTOM_VALUE
3988 CustomValue[x][y] = 0; /* initialized in InitField() */
3990 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3992 WasJustMoving[x][y] = 0;
3993 WasJustFalling[x][y] = 0;
3994 CheckCollision[x][y] = 0;
3995 CheckImpact[x][y] = 0;
3997 Pushed[x][y] = FALSE;
3999 ChangeCount[x][y] = 0;
4000 ChangeEvent[x][y] = -1;
4002 ExplodePhase[x][y] = 0;
4003 ExplodeDelay[x][y] = 0;
4004 ExplodeField[x][y] = EX_TYPE_NONE;
4006 RunnerVisit[x][y] = 0;
4007 PlayerVisit[x][y] = 0;
4010 GfxRandom[x][y] = INIT_GFX_RANDOM();
4011 GfxElement[x][y] = EL_UNDEFINED;
4012 GfxAction[x][y] = ACTION_DEFAULT;
4013 GfxDir[x][y] = MV_NONE;
4014 GfxRedraw[x][y] = GFX_REDRAW_NONE;
4017 SCAN_PLAYFIELD(x, y)
4019 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4021 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4023 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4026 InitField(x, y, TRUE);
4028 ResetGfxAnimation(x, y);
4033 for (i = 0; i < MAX_PLAYERS; i++)
4035 struct PlayerInfo *player = &stored_player[i];
4037 /* set number of special actions for bored and sleeping animation */
4038 player->num_special_action_bored =
4039 get_num_special_action(player->artwork_element,
4040 ACTION_BORING_1, ACTION_BORING_LAST);
4041 player->num_special_action_sleeping =
4042 get_num_special_action(player->artwork_element,
4043 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4046 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4047 emulate_sb ? EMU_SOKOBAN :
4048 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4050 #if USE_NEW_ALL_SLIPPERY
4051 /* initialize type of slippery elements */
4052 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4054 if (!IS_CUSTOM_ELEMENT(i))
4056 /* default: elements slip down either to the left or right randomly */
4057 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4059 /* SP style elements prefer to slip down on the left side */
4060 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4061 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4063 /* BD style elements prefer to slip down on the left side */
4064 if (game.emulation == EMU_BOULDERDASH)
4065 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4070 /* initialize explosion and ignition delay */
4071 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4073 if (!IS_CUSTOM_ELEMENT(i))
4076 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4077 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4078 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4079 int last_phase = (num_phase + 1) * delay;
4080 int half_phase = (num_phase / 2) * delay;
4082 element_info[i].explosion_delay = last_phase - 1;
4083 element_info[i].ignition_delay = half_phase;
4085 if (i == EL_BLACK_ORB)
4086 element_info[i].ignition_delay = 1;
4090 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4091 element_info[i].explosion_delay = 1;
4093 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4094 element_info[i].ignition_delay = 1;
4098 /* correct non-moving belts to start moving left */
4099 for (i = 0; i < NUM_BELTS; i++)
4100 if (game.belt_dir[i] == MV_NONE)
4101 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4103 #if USE_NEW_PLAYER_ASSIGNMENTS
4104 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4105 /* choose default local player */
4106 local_player = &stored_player[0];
4108 for (i = 0; i < MAX_PLAYERS; i++)
4109 stored_player[i].connected = FALSE;
4111 local_player->connected = TRUE;
4112 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4116 /* try to guess locally connected team mode players (needed for correct
4117 assignment of player figures from level to locally playing players) */
4119 for (i = 0; i < MAX_PLAYERS; i++)
4120 if (tape.player_participates[i])
4121 stored_player[i].connected = TRUE;
4123 else if (setup.team_mode && !options.network)
4125 /* try to guess locally connected team mode players (needed for correct
4126 assignment of player figures from level to locally playing players) */
4128 for (i = 0; i < MAX_PLAYERS; i++)
4129 if (setup.input[i].use_joystick ||
4130 setup.input[i].key.left != KSYM_UNDEFINED)
4131 stored_player[i].connected = TRUE;
4135 for (i = 0; i < MAX_PLAYERS; i++)
4136 printf("::: player %d: %s\n", i,
4137 (stored_player[i].connected ? "connected" : "not connected"));
4139 for (i = 0; i < MAX_PLAYERS; i++)
4140 printf("::: player %d: %s\n", i,
4141 (stored_player[i].present ? "present" : "not present"));
4144 /* check if any connected player was not found in playfield */
4145 for (i = 0; i < MAX_PLAYERS; i++)
4147 struct PlayerInfo *player = &stored_player[i];
4149 if (player->connected && !player->present)
4151 struct PlayerInfo *field_player = NULL;
4154 printf("::: looking for field player for player %d ...\n", i);
4157 /* assign first free player found that is present in the playfield */
4159 /* first try: look for unmapped playfield player that is not connected */
4160 if (field_player == NULL)
4161 for (j = 0; j < MAX_PLAYERS; j++)
4162 if (stored_player[j].present &&
4163 !stored_player[j].mapped &&
4164 !stored_player[j].connected)
4165 field_player = &stored_player[j];
4167 /* second try: look for *any* unmapped playfield player */
4168 if (field_player == NULL)
4169 for (j = 0; j < MAX_PLAYERS; j++)
4170 if (stored_player[j].present &&
4171 !stored_player[j].mapped)
4172 field_player = &stored_player[j];
4174 if (field_player != NULL)
4176 int jx = field_player->jx, jy = field_player->jy;
4179 printf("::: found player figure %d\n", field_player->index_nr);
4182 player->present = FALSE;
4183 player->active = FALSE;
4185 field_player->present = TRUE;
4186 field_player->active = TRUE;
4189 player->initial_element = field_player->initial_element;
4190 player->artwork_element = field_player->artwork_element;
4192 player->block_last_field = field_player->block_last_field;
4193 player->block_delay_adjustment = field_player->block_delay_adjustment;
4196 StorePlayer[jx][jy] = field_player->element_nr;
4198 field_player->jx = field_player->last_jx = jx;
4199 field_player->jy = field_player->last_jy = jy;
4201 if (local_player == player)
4202 local_player = field_player;
4204 map_player_action[field_player->index_nr] = i;
4206 field_player->mapped = TRUE;
4209 printf("::: map_player_action[%d] == %d\n",
4210 field_player->index_nr, i);
4215 if (player->connected && player->present)
4216 player->mapped = TRUE;
4221 /* check if any connected player was not found in playfield */
4222 for (i = 0; i < MAX_PLAYERS; i++)
4224 struct PlayerInfo *player = &stored_player[i];
4226 if (player->connected && !player->present)
4228 for (j = 0; j < MAX_PLAYERS; j++)
4230 struct PlayerInfo *field_player = &stored_player[j];
4231 int jx = field_player->jx, jy = field_player->jy;
4233 /* assign first free player found that is present in the playfield */
4234 if (field_player->present && !field_player->connected)
4236 player->present = TRUE;
4237 player->active = TRUE;
4239 field_player->present = FALSE;
4240 field_player->active = FALSE;
4242 player->initial_element = field_player->initial_element;
4243 player->artwork_element = field_player->artwork_element;
4245 player->block_last_field = field_player->block_last_field;
4246 player->block_delay_adjustment = field_player->block_delay_adjustment;
4248 StorePlayer[jx][jy] = player->element_nr;
4250 player->jx = player->last_jx = jx;
4251 player->jy = player->last_jy = jy;
4261 printf("::: local_player->present == %d\n", local_player->present);
4266 /* when playing a tape, eliminate all players who do not participate */
4268 #if USE_NEW_PLAYER_ASSIGNMENTS
4269 for (i = 0; i < MAX_PLAYERS; i++)
4271 if (stored_player[i].active &&
4272 !tape.player_participates[map_player_action[i]])
4274 struct PlayerInfo *player = &stored_player[i];
4275 int jx = player->jx, jy = player->jy;
4277 player->active = FALSE;
4278 StorePlayer[jx][jy] = 0;
4279 Feld[jx][jy] = EL_EMPTY;
4283 for (i = 0; i < MAX_PLAYERS; i++)
4285 if (stored_player[i].active &&
4286 !tape.player_participates[i])
4288 struct PlayerInfo *player = &stored_player[i];
4289 int jx = player->jx, jy = player->jy;
4291 player->active = FALSE;
4292 StorePlayer[jx][jy] = 0;
4293 Feld[jx][jy] = EL_EMPTY;
4298 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4300 /* when in single player mode, eliminate all but the first active player */
4302 for (i = 0; i < MAX_PLAYERS; i++)
4304 if (stored_player[i].active)
4306 for (j = i + 1; j < MAX_PLAYERS; j++)
4308 if (stored_player[j].active)
4310 struct PlayerInfo *player = &stored_player[j];
4311 int jx = player->jx, jy = player->jy;
4313 player->active = FALSE;
4314 player->present = FALSE;
4316 StorePlayer[jx][jy] = 0;
4317 Feld[jx][jy] = EL_EMPTY;
4324 /* when recording the game, store which players take part in the game */
4327 #if USE_NEW_PLAYER_ASSIGNMENTS
4328 for (i = 0; i < MAX_PLAYERS; i++)
4329 if (stored_player[i].connected)
4330 tape.player_participates[i] = TRUE;
4332 for (i = 0; i < MAX_PLAYERS; i++)
4333 if (stored_player[i].active)
4334 tape.player_participates[i] = TRUE;
4340 for (i = 0; i < MAX_PLAYERS; i++)
4342 struct PlayerInfo *player = &stored_player[i];
4344 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4349 if (local_player == player)
4350 printf("Player %d is local player.\n", i+1);
4354 if (BorderElement == EL_EMPTY)
4357 SBX_Right = lev_fieldx - SCR_FIELDX;
4359 SBY_Lower = lev_fieldy - SCR_FIELDY;
4364 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4366 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4371 if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4372 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4374 if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4375 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4377 if (EVEN(SCR_FIELDX))
4379 if (EVEN(SCR_FIELDY))
4384 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4385 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4387 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4388 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4391 /* if local player not found, look for custom element that might create
4392 the player (make some assumptions about the right custom element) */
4393 if (!local_player->present)
4395 int start_x = 0, start_y = 0;
4396 int found_rating = 0;
4397 int found_element = EL_UNDEFINED;
4398 int player_nr = local_player->index_nr;
4400 SCAN_PLAYFIELD(x, y)
4402 int element = Feld[x][y];
4407 if (level.use_start_element[player_nr] &&
4408 level.start_element[player_nr] == element &&
4415 found_element = element;
4418 if (!IS_CUSTOM_ELEMENT(element))
4421 if (CAN_CHANGE(element))
4423 for (i = 0; i < element_info[element].num_change_pages; i++)
4425 /* check for player created from custom element as single target */
4426 content = element_info[element].change_page[i].target_element;
4427 is_player = ELEM_IS_PLAYER(content);
4429 if (is_player && (found_rating < 3 ||
4430 (found_rating == 3 && element < found_element)))
4436 found_element = element;
4441 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4443 /* check for player created from custom element as explosion content */
4444 content = element_info[element].content.e[xx][yy];
4445 is_player = ELEM_IS_PLAYER(content);
4447 if (is_player && (found_rating < 2 ||
4448 (found_rating == 2 && element < found_element)))
4450 start_x = x + xx - 1;
4451 start_y = y + yy - 1;
4454 found_element = element;
4457 if (!CAN_CHANGE(element))
4460 for (i = 0; i < element_info[element].num_change_pages; i++)
4462 /* check for player created from custom element as extended target */
4464 element_info[element].change_page[i].target_content.e[xx][yy];
4466 is_player = ELEM_IS_PLAYER(content);
4468 if (is_player && (found_rating < 1 ||
4469 (found_rating == 1 && element < found_element)))
4471 start_x = x + xx - 1;
4472 start_y = y + yy - 1;
4475 found_element = element;
4481 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4482 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4485 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4486 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4491 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4492 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4493 local_player->jx - MIDPOSX);
4495 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4496 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4497 local_player->jy - MIDPOSY);
4501 printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4505 /* do not use PLAYING mask for fading out from main screen */
4506 game_status = GAME_MODE_MAIN;
4511 if (!game.restart_level)
4512 CloseDoor(DOOR_CLOSE_1);
4515 if (level_editor_test_game)
4516 FadeSkipNextFadeIn();
4518 FadeSetEnterScreen();
4520 if (level_editor_test_game)
4521 fading = fading_none;
4523 fading = menu.destination;
4527 FadeOut(REDRAW_FIELD);
4530 FadeOut(REDRAW_FIELD);
4534 game_status = GAME_MODE_PLAYING;
4537 /* !!! FIX THIS (START) !!! */
4538 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4540 InitGameEngine_EM();
4542 /* blit playfield from scroll buffer to normal back buffer for fading in */
4543 BlitScreenToBitmap_EM(backbuffer);
4545 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4547 InitGameEngine_SP();
4549 /* blit playfield from scroll buffer to normal back buffer for fading in */
4550 BlitScreenToBitmap_SP(backbuffer);
4557 /* after drawing the level, correct some elements */
4558 if (game.timegate_time_left == 0)
4559 CloseAllOpenTimegates();
4562 BlitScreenToBitmap(backbuffer);
4564 /* blit playfield from scroll buffer to normal back buffer for fading in */
4565 if (setup.soft_scrolling)
4566 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4569 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4571 /* !!! FIX THIS (END) !!! */
4574 FadeIn(REDRAW_FIELD);
4577 FadeIn(REDRAW_FIELD);
4582 if (!game.restart_level)
4584 /* copy default game door content to main double buffer */
4587 /* !!! CHECK AGAIN !!! */
4588 SetPanelBackground();
4589 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4590 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4592 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4594 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4595 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4596 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4597 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4600 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4601 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4605 SetPanelBackground();
4606 SetDrawBackgroundMask(REDRAW_DOOR_1);
4609 UpdateAndDisplayGameControlValues();
4611 UpdateGameDoorValues();
4612 DrawGameDoorValues();
4615 if (!game.restart_level)
4619 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4620 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4621 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4625 /* copy actual game door content to door double buffer for OpenDoor() */
4626 BlitBitmap(drawto, bitmap_db_door,
4627 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4629 OpenDoor(DOOR_OPEN_ALL);
4631 PlaySound(SND_GAME_STARTING);
4633 if (setup.sound_music)
4636 KeyboardAutoRepeatOffUnlessAutoplay();
4640 for (i = 0; i < MAX_PLAYERS; i++)
4641 printf("Player %d %sactive.\n",
4642 i + 1, (stored_player[i].active ? "" : "not "));
4653 if (!game.restart_level && !tape.playing)
4655 LevelStats_incPlayed(level_nr);
4657 SaveLevelSetup_SeriesInfo();
4660 printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4664 game.restart_level = FALSE;
4667 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4669 /* this is used for non-R'n'D game engines to update certain engine values */
4671 /* needed to determine if sounds are played within the visible screen area */
4672 scroll_x = actual_scroll_x;
4673 scroll_y = actual_scroll_y;
4676 void InitMovDir(int x, int y)
4678 int i, element = Feld[x][y];
4679 static int xy[4][2] =
4686 static int direction[3][4] =
4688 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4689 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4690 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4699 Feld[x][y] = EL_BUG;
4700 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4703 case EL_SPACESHIP_RIGHT:
4704 case EL_SPACESHIP_UP:
4705 case EL_SPACESHIP_LEFT:
4706 case EL_SPACESHIP_DOWN:
4707 Feld[x][y] = EL_SPACESHIP;
4708 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4711 case EL_BD_BUTTERFLY_RIGHT:
4712 case EL_BD_BUTTERFLY_UP:
4713 case EL_BD_BUTTERFLY_LEFT:
4714 case EL_BD_BUTTERFLY_DOWN:
4715 Feld[x][y] = EL_BD_BUTTERFLY;
4716 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4719 case EL_BD_FIREFLY_RIGHT:
4720 case EL_BD_FIREFLY_UP:
4721 case EL_BD_FIREFLY_LEFT:
4722 case EL_BD_FIREFLY_DOWN:
4723 Feld[x][y] = EL_BD_FIREFLY;
4724 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4727 case EL_PACMAN_RIGHT:
4729 case EL_PACMAN_LEFT:
4730 case EL_PACMAN_DOWN:
4731 Feld[x][y] = EL_PACMAN;
4732 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4735 case EL_YAMYAM_LEFT:
4736 case EL_YAMYAM_RIGHT:
4738 case EL_YAMYAM_DOWN:
4739 Feld[x][y] = EL_YAMYAM;
4740 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4743 case EL_SP_SNIKSNAK:
4744 MovDir[x][y] = MV_UP;
4747 case EL_SP_ELECTRON:
4748 MovDir[x][y] = MV_LEFT;
4755 Feld[x][y] = EL_MOLE;
4756 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4760 if (IS_CUSTOM_ELEMENT(element))
4762 struct ElementInfo *ei = &element_info[element];
4763 int move_direction_initial = ei->move_direction_initial;
4764 int move_pattern = ei->move_pattern;
4766 if (move_direction_initial == MV_START_PREVIOUS)
4768 if (MovDir[x][y] != MV_NONE)
4771 move_direction_initial = MV_START_AUTOMATIC;
4774 if (move_direction_initial == MV_START_RANDOM)
4775 MovDir[x][y] = 1 << RND(4);
4776 else if (move_direction_initial & MV_ANY_DIRECTION)
4777 MovDir[x][y] = move_direction_initial;
4778 else if (move_pattern == MV_ALL_DIRECTIONS ||
4779 move_pattern == MV_TURNING_LEFT ||
4780 move_pattern == MV_TURNING_RIGHT ||
4781 move_pattern == MV_TURNING_LEFT_RIGHT ||
4782 move_pattern == MV_TURNING_RIGHT_LEFT ||
4783 move_pattern == MV_TURNING_RANDOM)
4784 MovDir[x][y] = 1 << RND(4);
4785 else if (move_pattern == MV_HORIZONTAL)
4786 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4787 else if (move_pattern == MV_VERTICAL)
4788 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4789 else if (move_pattern & MV_ANY_DIRECTION)
4790 MovDir[x][y] = element_info[element].move_pattern;
4791 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4792 move_pattern == MV_ALONG_RIGHT_SIDE)
4794 /* use random direction as default start direction */
4795 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4796 MovDir[x][y] = 1 << RND(4);
4798 for (i = 0; i < NUM_DIRECTIONS; i++)
4800 int x1 = x + xy[i][0];
4801 int y1 = y + xy[i][1];
4803 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4805 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4806 MovDir[x][y] = direction[0][i];
4808 MovDir[x][y] = direction[1][i];
4817 MovDir[x][y] = 1 << RND(4);
4819 if (element != EL_BUG &&
4820 element != EL_SPACESHIP &&
4821 element != EL_BD_BUTTERFLY &&
4822 element != EL_BD_FIREFLY)
4825 for (i = 0; i < NUM_DIRECTIONS; i++)
4827 int x1 = x + xy[i][0];
4828 int y1 = y + xy[i][1];
4830 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4832 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4834 MovDir[x][y] = direction[0][i];
4837 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4838 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4840 MovDir[x][y] = direction[1][i];
4849 GfxDir[x][y] = MovDir[x][y];
4852 void InitAmoebaNr(int x, int y)
4855 int group_nr = AmoebeNachbarNr(x, y);
4859 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4861 if (AmoebaCnt[i] == 0)
4869 AmoebaNr[x][y] = group_nr;
4870 AmoebaCnt[group_nr]++;
4871 AmoebaCnt2[group_nr]++;
4874 static void PlayerWins(struct PlayerInfo *player)
4876 player->LevelSolved = TRUE;
4877 player->GameOver = TRUE;
4879 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4880 level.native_em_level->lev->score : player->score);
4882 player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4884 player->LevelSolved_CountingScore = player->score_final;
4889 static int time, time_final;
4890 static int score, score_final;
4891 static int game_over_delay_1 = 0;
4892 static int game_over_delay_2 = 0;
4893 int game_over_delay_value_1 = 50;
4894 int game_over_delay_value_2 = 50;
4896 if (!local_player->LevelSolved_GameWon)
4900 /* do not start end game actions before the player stops moving (to exit) */
4901 if (local_player->MovPos)
4904 local_player->LevelSolved_GameWon = TRUE;
4905 local_player->LevelSolved_SaveTape = tape.recording;
4906 local_player->LevelSolved_SaveScore = !tape.playing;
4910 LevelStats_incSolved(level_nr);
4912 SaveLevelSetup_SeriesInfo();
4915 printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4919 if (tape.auto_play) /* tape might already be stopped here */
4920 tape.auto_play_level_solved = TRUE;
4926 game_over_delay_1 = game_over_delay_value_1;
4927 game_over_delay_2 = game_over_delay_value_2;
4929 time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4930 score = score_final = local_player->score_final;
4935 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4937 else if (game.no_time_limit && TimePlayed < 999)
4940 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4943 local_player->score_final = score_final;
4945 if (level_editor_test_game)
4948 score = score_final;
4951 local_player->LevelSolved_CountingTime = time;
4952 local_player->LevelSolved_CountingScore = score;
4954 game_panel_controls[GAME_PANEL_TIME].value = time;
4955 game_panel_controls[GAME_PANEL_SCORE].value = score;
4957 DisplayGameControlValues();
4959 DrawGameValue_Time(time);
4960 DrawGameValue_Score(score);
4964 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4966 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4968 /* close exit door after last player */
4969 if ((AllPlayersGone &&
4970 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4971 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4972 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4973 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4974 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4976 int element = Feld[ExitX][ExitY];
4979 if (element == EL_EM_EXIT_OPEN ||
4980 element == EL_EM_STEEL_EXIT_OPEN)
4987 Feld[ExitX][ExitY] =
4988 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4989 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4990 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4991 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4992 EL_EM_STEEL_EXIT_CLOSING);
4994 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4998 /* player disappears */
4999 DrawLevelField(ExitX, ExitY);
5002 for (i = 0; i < MAX_PLAYERS; i++)
5004 struct PlayerInfo *player = &stored_player[i];
5006 if (player->present)
5008 RemovePlayer(player);
5010 /* player disappears */
5011 DrawLevelField(player->jx, player->jy);
5016 PlaySound(SND_GAME_WINNING);
5019 if (game_over_delay_1 > 0)
5021 game_over_delay_1--;
5026 if (time != time_final)
5028 int time_to_go = ABS(time_final - time);
5029 int time_count_dir = (time < time_final ? +1 : -1);
5030 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
5032 time += time_count_steps * time_count_dir;
5033 score += time_count_steps * level.score[SC_TIME_BONUS];
5036 local_player->LevelSolved_CountingTime = time;
5037 local_player->LevelSolved_CountingScore = score;
5039 game_panel_controls[GAME_PANEL_TIME].value = time;
5040 game_panel_controls[GAME_PANEL_SCORE].value = score;
5042 DisplayGameControlValues();
5044 DrawGameValue_Time(time);
5045 DrawGameValue_Score(score);
5048 if (time == time_final)
5049 StopSound(SND_GAME_LEVELTIME_BONUS);
5050 else if (setup.sound_loops)
5051 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5053 PlaySound(SND_GAME_LEVELTIME_BONUS);
5058 local_player->LevelSolved_PanelOff = TRUE;
5060 if (game_over_delay_2 > 0)
5062 game_over_delay_2--;
5075 boolean raise_level = FALSE;
5077 local_player->LevelSolved_GameEnd = TRUE;
5079 CloseDoor(DOOR_CLOSE_1);
5081 if (local_player->LevelSolved_SaveTape)
5088 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5090 SaveTape(tape.level_nr); /* ask to save tape */
5094 if (level_editor_test_game)
5096 game_status = GAME_MODE_MAIN;
5099 DrawAndFadeInMainMenu(REDRAW_FIELD);
5107 if (!local_player->LevelSolved_SaveScore)
5110 FadeOut(REDRAW_FIELD);
5113 game_status = GAME_MODE_MAIN;
5115 DrawAndFadeInMainMenu(REDRAW_FIELD);
5120 if (level_nr == leveldir_current->handicap_level)
5122 leveldir_current->handicap_level++;
5124 SaveLevelSetup_SeriesInfo();
5127 if (level_nr < leveldir_current->last_level)
5128 raise_level = TRUE; /* advance to next level */
5130 if ((hi_pos = NewHiScore()) >= 0)
5132 game_status = GAME_MODE_SCORES;
5134 DrawHallOfFame(hi_pos);
5145 FadeOut(REDRAW_FIELD);
5148 game_status = GAME_MODE_MAIN;
5156 DrawAndFadeInMainMenu(REDRAW_FIELD);
5165 LoadScore(level_nr);
5167 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5168 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5171 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5173 if (local_player->score_final > highscore[k].Score)
5175 /* player has made it to the hall of fame */
5177 if (k < MAX_SCORE_ENTRIES - 1)
5179 int m = MAX_SCORE_ENTRIES - 1;
5182 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5183 if (strEqual(setup.player_name, highscore[l].Name))
5185 if (m == k) /* player's new highscore overwrites his old one */
5189 for (l = m; l > k; l--)
5191 strcpy(highscore[l].Name, highscore[l - 1].Name);
5192 highscore[l].Score = highscore[l - 1].Score;
5199 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5200 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5201 highscore[k].Score = local_player->score_final;
5207 else if (!strncmp(setup.player_name, highscore[k].Name,
5208 MAX_PLAYER_NAME_LEN))
5209 break; /* player already there with a higher score */
5215 SaveScore(level_nr);
5220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5222 int element = Feld[x][y];
5223 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5224 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5225 int horiz_move = (dx != 0);
5226 int sign = (horiz_move ? dx : dy);
5227 int step = sign * element_info[element].move_stepsize;
5229 /* special values for move stepsize for spring and things on conveyor belt */
5232 if (CAN_FALL(element) &&
5233 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5234 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5235 else if (element == EL_SPRING)
5236 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5242 inline static int getElementMoveStepsize(int x, int y)
5244 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5249 if (player->GfxAction != action || player->GfxDir != dir)
5252 printf("Player frame reset! (%d => %d, %d => %d)\n",
5253 player->GfxAction, action, player->GfxDir, dir);
5256 player->GfxAction = action;
5257 player->GfxDir = dir;
5259 player->StepFrame = 0;
5263 #if USE_GFX_RESET_GFX_ANIMATION
5264 static void ResetGfxFrame(int x, int y, boolean redraw)
5266 int element = Feld[x][y];
5267 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5268 int last_gfx_frame = GfxFrame[x][y];
5270 if (graphic_info[graphic].anim_global_sync)
5271 GfxFrame[x][y] = FrameCounter;
5272 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5273 GfxFrame[x][y] = CustomValue[x][y];
5274 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5275 GfxFrame[x][y] = element_info[element].collect_score;
5276 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5277 GfxFrame[x][y] = ChangeDelay[x][y];
5279 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5280 DrawLevelGraphicAnimation(x, y, graphic);
5284 static void ResetGfxAnimation(int x, int y)
5286 GfxAction[x][y] = ACTION_DEFAULT;
5287 GfxDir[x][y] = MovDir[x][y];
5290 #if USE_GFX_RESET_GFX_ANIMATION
5291 ResetGfxFrame(x, y, FALSE);
5295 static void ResetRandomAnimationValue(int x, int y)
5297 GfxRandom[x][y] = INIT_GFX_RANDOM();
5300 void InitMovingField(int x, int y, int direction)
5302 int element = Feld[x][y];
5303 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5304 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5307 boolean is_moving_before, is_moving_after;
5309 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5312 /* check if element was/is moving or being moved before/after mode change */
5315 is_moving_before = (WasJustMoving[x][y] != 0);
5317 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5318 is_moving_before = WasJustMoving[x][y];
5321 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5323 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5325 /* reset animation only for moving elements which change direction of moving
5326 or which just started or stopped moving
5327 (else CEs with property "can move" / "not moving" are reset each frame) */
5328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5330 if (is_moving_before != is_moving_after ||
5331 direction != MovDir[x][y])
5332 ResetGfxAnimation(x, y);
5334 if ((is_moving_before || is_moving_after) && !continues_moving)
5335 ResetGfxAnimation(x, y);
5338 if (!continues_moving)
5339 ResetGfxAnimation(x, y);
5342 MovDir[x][y] = direction;
5343 GfxDir[x][y] = direction;
5345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5346 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5347 direction == MV_DOWN && CAN_FALL(element) ?
5348 ACTION_FALLING : ACTION_MOVING);
5350 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5351 ACTION_FALLING : ACTION_MOVING);
5354 /* this is needed for CEs with property "can move" / "not moving" */
5356 if (is_moving_after)
5358 if (Feld[newx][newy] == EL_EMPTY)
5359 Feld[newx][newy] = EL_BLOCKED;
5361 MovDir[newx][newy] = MovDir[x][y];
5363 #if USE_NEW_CUSTOM_VALUE
5364 CustomValue[newx][newy] = CustomValue[x][y];
5367 GfxFrame[newx][newy] = GfxFrame[x][y];
5368 GfxRandom[newx][newy] = GfxRandom[x][y];
5369 GfxAction[newx][newy] = GfxAction[x][y];
5370 GfxDir[newx][newy] = GfxDir[x][y];
5374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5376 int direction = MovDir[x][y];
5377 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5378 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5386 int oldx = x, oldy = y;
5387 int direction = MovDir[x][y];
5389 if (direction == MV_LEFT)
5391 else if (direction == MV_RIGHT)
5393 else if (direction == MV_UP)
5395 else if (direction == MV_DOWN)
5398 *comes_from_x = oldx;
5399 *comes_from_y = oldy;
5402 int MovingOrBlocked2Element(int x, int y)
5404 int element = Feld[x][y];
5406 if (element == EL_BLOCKED)
5410 Blocked2Moving(x, y, &oldx, &oldy);
5411 return Feld[oldx][oldy];
5417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5419 /* like MovingOrBlocked2Element(), but if element is moving
5420 and (x,y) is the field the moving element is just leaving,
5421 return EL_BLOCKED instead of the element value */
5422 int element = Feld[x][y];
5424 if (IS_MOVING(x, y))
5426 if (element == EL_BLOCKED)
5430 Blocked2Moving(x, y, &oldx, &oldy);
5431 return Feld[oldx][oldy];
5440 static void RemoveField(int x, int y)
5442 Feld[x][y] = EL_EMPTY;
5448 #if USE_NEW_CUSTOM_VALUE
5449 CustomValue[x][y] = 0;
5453 ChangeDelay[x][y] = 0;
5454 ChangePage[x][y] = -1;
5455 Pushed[x][y] = FALSE;
5458 ExplodeField[x][y] = EX_TYPE_NONE;
5461 GfxElement[x][y] = EL_UNDEFINED;
5462 GfxAction[x][y] = ACTION_DEFAULT;
5463 GfxDir[x][y] = MV_NONE;
5465 /* !!! this would prevent the removed tile from being redrawn !!! */
5466 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5470 void RemoveMovingField(int x, int y)
5472 int oldx = x, oldy = y, newx = x, newy = y;
5473 int element = Feld[x][y];
5474 int next_element = EL_UNDEFINED;
5476 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5479 if (IS_MOVING(x, y))
5481 Moving2Blocked(x, y, &newx, &newy);
5483 if (Feld[newx][newy] != EL_BLOCKED)
5485 /* element is moving, but target field is not free (blocked), but
5486 already occupied by something different (example: acid pool);
5487 in this case, only remove the moving field, but not the target */
5489 RemoveField(oldx, oldy);
5491 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5493 TEST_DrawLevelField(oldx, oldy);
5498 else if (element == EL_BLOCKED)
5500 Blocked2Moving(x, y, &oldx, &oldy);
5501 if (!IS_MOVING(oldx, oldy))
5505 if (element == EL_BLOCKED &&
5506 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5507 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5508 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5509 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5510 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5511 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5512 next_element = get_next_element(Feld[oldx][oldy]);
5514 RemoveField(oldx, oldy);
5515 RemoveField(newx, newy);
5517 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5519 if (next_element != EL_UNDEFINED)
5520 Feld[oldx][oldy] = next_element;
5522 TEST_DrawLevelField(oldx, oldy);
5523 TEST_DrawLevelField(newx, newy);
5526 void DrawDynamite(int x, int y)
5528 int sx = SCREENX(x), sy = SCREENY(y);
5529 int graphic = el2img(Feld[x][y]);
5532 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5535 if (IS_WALKABLE_INSIDE(Back[x][y]))
5539 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5540 else if (Store[x][y])
5541 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5543 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5545 if (Back[x][y] || Store[x][y])
5546 DrawGraphicThruMask(sx, sy, graphic, frame);
5548 DrawGraphic(sx, sy, graphic, frame);
5551 void CheckDynamite(int x, int y)
5553 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5557 if (MovDelay[x][y] != 0)
5560 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5566 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5571 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5573 boolean num_checked_players = 0;
5576 for (i = 0; i < MAX_PLAYERS; i++)
5578 if (stored_player[i].active)
5580 int sx = stored_player[i].jx;
5581 int sy = stored_player[i].jy;
5583 if (num_checked_players == 0)
5590 *sx1 = MIN(*sx1, sx);
5591 *sy1 = MIN(*sy1, sy);
5592 *sx2 = MAX(*sx2, sx);
5593 *sy2 = MAX(*sy2, sy);
5596 num_checked_players++;
5601 static boolean checkIfAllPlayersFitToScreen_RND()
5603 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5605 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5607 return (sx2 - sx1 < SCR_FIELDX &&
5608 sy2 - sy1 < SCR_FIELDY);
5611 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5613 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5615 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5617 *sx = (sx1 + sx2) / 2;
5618 *sy = (sy1 + sy2) / 2;
5621 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5622 boolean center_screen, boolean quick_relocation)
5624 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625 boolean no_delay = (tape.warp_forward);
5626 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5629 if (quick_relocation)
5631 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5633 if (!level.shifted_relocation || center_screen)
5635 /* quick relocation (without scrolling), with centering of screen */
5637 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5638 x > SBX_Right + MIDPOSX ? SBX_Right :
5641 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5642 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5647 /* quick relocation (without scrolling), but do not center screen */
5649 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5650 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5653 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5654 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5657 int offset_x = x + (scroll_x - center_scroll_x);
5658 int offset_y = y + (scroll_y - center_scroll_y);
5660 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5661 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5662 offset_x - MIDPOSX);
5664 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5665 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5666 offset_y - MIDPOSY);
5672 if (!level.shifted_relocation || center_screen)
5674 /* quick relocation (without scrolling), with centering of screen */
5676 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5677 x > SBX_Right + MIDPOSX ? SBX_Right :
5680 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5681 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5686 /* quick relocation (without scrolling), but do not center screen */
5688 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5689 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5692 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5693 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5696 int offset_x = x + (scroll_x - center_scroll_x);
5697 int offset_y = y + (scroll_y - center_scroll_y);
5699 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5700 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5701 offset_x - MIDPOSX);
5703 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5704 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5705 offset_y - MIDPOSY);
5708 /* quick relocation (without scrolling), inside visible screen area */
5710 int offset = game.scroll_delay_value;
5712 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5713 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5714 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5716 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5717 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5718 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5720 /* don't scroll over playfield boundaries */
5721 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5722 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5724 /* don't scroll over playfield boundaries */
5725 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5726 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5730 RedrawPlayfield(TRUE, 0,0,0,0);
5735 int scroll_xx, scroll_yy;
5737 if (!level.shifted_relocation || center_screen)
5739 /* visible relocation (with scrolling), with centering of screen */
5741 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5742 x > SBX_Right + MIDPOSX ? SBX_Right :
5745 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5746 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5751 /* visible relocation (with scrolling), but do not center screen */
5753 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5754 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5757 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5758 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5761 int offset_x = x + (scroll_x - center_scroll_x);
5762 int offset_y = y + (scroll_y - center_scroll_y);
5764 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5765 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5766 offset_x - MIDPOSX);
5768 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5769 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5770 offset_y - MIDPOSY);
5775 /* visible relocation (with scrolling), with centering of screen */
5777 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5778 x > SBX_Right + MIDPOSX ? SBX_Right :
5781 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5782 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5786 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5788 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5791 int fx = FX, fy = FY;
5793 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5794 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5796 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5802 fx += dx * TILEX / 2;
5803 fy += dy * TILEY / 2;
5805 ScrollLevel(dx, dy);
5808 /* scroll in two steps of half tile size to make things smoother */
5809 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5811 Delay(wait_delay_value);
5813 /* scroll second step to align at full tile size */
5815 Delay(wait_delay_value);
5820 Delay(wait_delay_value);
5824 void RelocatePlayer(int jx, int jy, int el_player_raw)
5826 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5827 int player_nr = GET_PLAYER_NR(el_player);
5828 struct PlayerInfo *player = &stored_player[player_nr];
5829 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5830 boolean no_delay = (tape.warp_forward);
5831 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5832 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5833 int old_jx = player->jx;
5834 int old_jy = player->jy;
5835 int old_element = Feld[old_jx][old_jy];
5836 int element = Feld[jx][jy];
5837 boolean player_relocated = (old_jx != jx || old_jy != jy);
5839 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5840 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5841 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5842 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5843 int leave_side_horiz = move_dir_horiz;
5844 int leave_side_vert = move_dir_vert;
5845 int enter_side = enter_side_horiz | enter_side_vert;
5846 int leave_side = leave_side_horiz | leave_side_vert;
5848 if (player->GameOver) /* do not reanimate dead player */
5851 if (!player_relocated) /* no need to relocate the player */
5854 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5856 RemoveField(jx, jy); /* temporarily remove newly placed player */
5857 DrawLevelField(jx, jy);
5860 if (player->present)
5862 while (player->MovPos)
5864 ScrollPlayer(player, SCROLL_GO_ON);
5865 ScrollScreen(NULL, SCROLL_GO_ON);
5867 AdvanceFrameAndPlayerCounters(player->index_nr);
5872 Delay(wait_delay_value);
5875 DrawPlayer(player); /* needed here only to cleanup last field */
5876 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5878 player->is_moving = FALSE;
5881 if (IS_CUSTOM_ELEMENT(old_element))
5882 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5884 player->index_bit, leave_side);
5886 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5888 player->index_bit, leave_side);
5890 Feld[jx][jy] = el_player;
5891 InitPlayerField(jx, jy, el_player, TRUE);
5893 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5894 possible that the relocation target field did not contain a player element,
5895 but a walkable element, to which the new player was relocated -- in this
5896 case, restore that (already initialized!) element on the player field */
5897 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5899 Feld[jx][jy] = element; /* restore previously existing element */
5901 /* !!! do not initialize already initialized element a second time !!! */
5902 /* (this causes at least problems with "element creation" CE trigger for
5903 already existing elements, and existing Sokoban fields counted twice) */
5904 InitField(jx, jy, FALSE);
5908 /* only visually relocate centered player */
5909 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5910 FALSE, level.instant_relocation);
5912 TestIfPlayerTouchesBadThing(jx, jy);
5913 TestIfPlayerTouchesCustomElement(jx, jy);
5915 if (IS_CUSTOM_ELEMENT(element))
5916 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5917 player->index_bit, enter_side);
5919 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5920 player->index_bit, enter_side);
5923 if (player->is_switching)
5925 /* ensure that relocation while still switching an element does not cause
5926 a new element to be treated as also switched directly after relocation
5927 (this is important for teleporter switches that teleport the player to
5928 a place where another teleporter switch is in the same direction, which
5929 would then incorrectly be treated as immediately switched before the
5930 direction key that caused the switch was released) */
5932 player->switch_x += jx - old_jx;
5933 player->switch_y += jy - old_jy;
5938 void Explode(int ex, int ey, int phase, int mode)
5944 /* !!! eliminate this variable !!! */
5945 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5947 if (game.explosions_delayed)
5949 ExplodeField[ex][ey] = mode;
5953 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5955 int center_element = Feld[ex][ey];
5956 int artwork_element, explosion_element; /* set these values later */
5959 /* --- This is only really needed (and now handled) in "Impact()". --- */
5960 /* do not explode moving elements that left the explode field in time */
5961 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5962 center_element == EL_EMPTY &&
5963 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5968 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5969 if (mode == EX_TYPE_NORMAL ||
5970 mode == EX_TYPE_CENTER ||
5971 mode == EX_TYPE_CROSS)
5972 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5975 /* remove things displayed in background while burning dynamite */
5976 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5979 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5981 /* put moving element to center field (and let it explode there) */
5982 center_element = MovingOrBlocked2Element(ex, ey);
5983 RemoveMovingField(ex, ey);
5984 Feld[ex][ey] = center_element;
5987 /* now "center_element" is finally determined -- set related values now */
5988 artwork_element = center_element; /* for custom player artwork */
5989 explosion_element = center_element; /* for custom player artwork */
5991 if (IS_PLAYER(ex, ey))
5993 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5995 artwork_element = stored_player[player_nr].artwork_element;
5997 if (level.use_explosion_element[player_nr])
5999 explosion_element = level.explosion_element[player_nr];
6000 artwork_element = explosion_element;
6005 if (mode == EX_TYPE_NORMAL ||
6006 mode == EX_TYPE_CENTER ||
6007 mode == EX_TYPE_CROSS)
6008 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6011 last_phase = element_info[explosion_element].explosion_delay + 1;
6013 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6015 int xx = x - ex + 1;
6016 int yy = y - ey + 1;
6019 if (!IN_LEV_FIELD(x, y) ||
6020 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6021 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
6024 element = Feld[x][y];
6026 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6028 element = MovingOrBlocked2Element(x, y);
6030 if (!IS_EXPLOSION_PROOF(element))
6031 RemoveMovingField(x, y);
6034 /* indestructible elements can only explode in center (but not flames) */
6035 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6036 mode == EX_TYPE_BORDER)) ||
6037 element == EL_FLAMES)
6040 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6041 behaviour, for example when touching a yamyam that explodes to rocks
6042 with active deadly shield, a rock is created under the player !!! */
6043 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6045 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6046 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6047 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6049 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6052 if (IS_ACTIVE_BOMB(element))
6054 /* re-activate things under the bomb like gate or penguin */
6055 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6062 /* save walkable background elements while explosion on same tile */
6063 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6064 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6065 Back[x][y] = element;
6067 /* ignite explodable elements reached by other explosion */
6068 if (element == EL_EXPLOSION)
6069 element = Store2[x][y];
6071 if (AmoebaNr[x][y] &&
6072 (element == EL_AMOEBA_FULL ||
6073 element == EL_BD_AMOEBA ||
6074 element == EL_AMOEBA_GROWING))
6076 AmoebaCnt[AmoebaNr[x][y]]--;
6077 AmoebaCnt2[AmoebaNr[x][y]]--;
6082 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6084 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6086 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6088 if (PLAYERINFO(ex, ey)->use_murphy)
6089 Store[x][y] = EL_EMPTY;
6092 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6093 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6094 else if (ELEM_IS_PLAYER(center_element))
6095 Store[x][y] = EL_EMPTY;
6096 else if (center_element == EL_YAMYAM)
6097 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6098 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6099 Store[x][y] = element_info[center_element].content.e[xx][yy];
6101 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6102 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6103 otherwise) -- FIX THIS !!! */
6104 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6105 Store[x][y] = element_info[element].content.e[1][1];
6107 else if (!CAN_EXPLODE(element))
6108 Store[x][y] = element_info[element].content.e[1][1];
6111 Store[x][y] = EL_EMPTY;
6113 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6114 center_element == EL_AMOEBA_TO_DIAMOND)
6115 Store2[x][y] = element;
6117 Feld[x][y] = EL_EXPLOSION;
6118 GfxElement[x][y] = artwork_element;
6120 ExplodePhase[x][y] = 1;
6121 ExplodeDelay[x][y] = last_phase;
6126 if (center_element == EL_YAMYAM)
6127 game.yamyam_content_nr =
6128 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6140 GfxFrame[x][y] = 0; /* restart explosion animation */
6142 last_phase = ExplodeDelay[x][y];
6144 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6148 /* activate this even in non-DEBUG version until cause for crash in
6149 getGraphicAnimationFrame() (see below) is found and eliminated */
6155 /* this can happen if the player leaves an explosion just in time */
6156 if (GfxElement[x][y] == EL_UNDEFINED)
6157 GfxElement[x][y] = EL_EMPTY;
6159 if (GfxElement[x][y] == EL_UNDEFINED)
6162 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6163 printf("Explode(): This should never happen!\n");
6166 GfxElement[x][y] = EL_EMPTY;
6172 border_element = Store2[x][y];
6173 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6174 border_element = StorePlayer[x][y];
6176 if (phase == element_info[border_element].ignition_delay ||
6177 phase == last_phase)
6179 boolean border_explosion = FALSE;
6181 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6182 !PLAYER_EXPLOSION_PROTECTED(x, y))
6184 KillPlayerUnlessExplosionProtected(x, y);
6185 border_explosion = TRUE;
6187 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6189 Feld[x][y] = Store2[x][y];
6192 border_explosion = TRUE;
6194 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6196 AmoebeUmwandeln(x, y);
6198 border_explosion = TRUE;
6201 /* if an element just explodes due to another explosion (chain-reaction),
6202 do not immediately end the new explosion when it was the last frame of
6203 the explosion (as it would be done in the following "if"-statement!) */
6204 if (border_explosion && phase == last_phase)
6208 if (phase == last_phase)
6212 element = Feld[x][y] = Store[x][y];
6213 Store[x][y] = Store2[x][y] = 0;
6214 GfxElement[x][y] = EL_UNDEFINED;
6216 /* player can escape from explosions and might therefore be still alive */
6217 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6218 element <= EL_PLAYER_IS_EXPLODING_4)
6220 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6221 int explosion_element = EL_PLAYER_1 + player_nr;
6222 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6223 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6225 if (level.use_explosion_element[player_nr])
6226 explosion_element = level.explosion_element[player_nr];
6228 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6229 element_info[explosion_element].content.e[xx][yy]);
6232 /* restore probably existing indestructible background element */
6233 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6234 element = Feld[x][y] = Back[x][y];
6237 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6238 GfxDir[x][y] = MV_NONE;
6239 ChangeDelay[x][y] = 0;
6240 ChangePage[x][y] = -1;
6242 #if USE_NEW_CUSTOM_VALUE
6243 CustomValue[x][y] = 0;
6246 InitField_WithBug2(x, y, FALSE);
6248 TEST_DrawLevelField(x, y);
6250 TestIfElementTouchesCustomElement(x, y);
6252 if (GFX_CRUMBLED(element))
6253 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6255 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6256 StorePlayer[x][y] = 0;
6258 if (ELEM_IS_PLAYER(element))
6259 RelocatePlayer(x, y, element);
6261 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6263 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6264 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6267 TEST_DrawLevelFieldCrumbled(x, y);
6269 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6271 DrawLevelElement(x, y, Back[x][y]);
6272 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6274 else if (IS_WALKABLE_UNDER(Back[x][y]))
6276 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6277 DrawLevelElementThruMask(x, y, Back[x][y]);
6279 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6280 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6284 void DynaExplode(int ex, int ey)
6287 int dynabomb_element = Feld[ex][ey];
6288 int dynabomb_size = 1;
6289 boolean dynabomb_xl = FALSE;
6290 struct PlayerInfo *player;
6291 static int xy[4][2] =
6299 if (IS_ACTIVE_BOMB(dynabomb_element))
6301 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6302 dynabomb_size = player->dynabomb_size;
6303 dynabomb_xl = player->dynabomb_xl;
6304 player->dynabombs_left++;
6307 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6309 for (i = 0; i < NUM_DIRECTIONS; i++)
6311 for (j = 1; j <= dynabomb_size; j++)
6313 int x = ex + j * xy[i][0];
6314 int y = ey + j * xy[i][1];
6317 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6320 element = Feld[x][y];
6322 /* do not restart explosions of fields with active bombs */
6323 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6326 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6328 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6329 !IS_DIGGABLE(element) && !dynabomb_xl)
6335 void Bang(int x, int y)
6337 int element = MovingOrBlocked2Element(x, y);
6338 int explosion_type = EX_TYPE_NORMAL;
6340 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6342 struct PlayerInfo *player = PLAYERINFO(x, y);
6344 #if USE_FIX_CE_ACTION_WITH_PLAYER
6345 element = Feld[x][y] = player->initial_element;
6347 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6348 player->element_nr);
6351 if (level.use_explosion_element[player->index_nr])
6353 int explosion_element = level.explosion_element[player->index_nr];
6355 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6356 explosion_type = EX_TYPE_CROSS;
6357 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6358 explosion_type = EX_TYPE_CENTER;
6366 case EL_BD_BUTTERFLY:
6369 case EL_DARK_YAMYAM:
6373 RaiseScoreElement(element);
6376 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6377 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6378 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6379 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6380 case EL_DYNABOMB_INCREASE_NUMBER:
6381 case EL_DYNABOMB_INCREASE_SIZE:
6382 case EL_DYNABOMB_INCREASE_POWER:
6383 explosion_type = EX_TYPE_DYNA;
6386 case EL_DC_LANDMINE:
6388 case EL_EM_EXIT_OPEN:
6389 case EL_EM_STEEL_EXIT_OPEN:
6391 explosion_type = EX_TYPE_CENTER;
6396 case EL_LAMP_ACTIVE:
6397 case EL_AMOEBA_TO_DIAMOND:
6398 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6399 explosion_type = EX_TYPE_CENTER;
6403 if (element_info[element].explosion_type == EXPLODES_CROSS)
6404 explosion_type = EX_TYPE_CROSS;
6405 else if (element_info[element].explosion_type == EXPLODES_1X1)
6406 explosion_type = EX_TYPE_CENTER;
6410 if (explosion_type == EX_TYPE_DYNA)
6413 Explode(x, y, EX_PHASE_START, explosion_type);
6415 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6418 void SplashAcid(int x, int y)
6420 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6421 (!IN_LEV_FIELD(x - 1, y - 2) ||
6422 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6423 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6425 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6426 (!IN_LEV_FIELD(x + 1, y - 2) ||
6427 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6428 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6430 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6433 static void InitBeltMovement()
6435 static int belt_base_element[4] =
6437 EL_CONVEYOR_BELT_1_LEFT,
6438 EL_CONVEYOR_BELT_2_LEFT,
6439 EL_CONVEYOR_BELT_3_LEFT,
6440 EL_CONVEYOR_BELT_4_LEFT
6442 static int belt_base_active_element[4] =
6444 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6445 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6446 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6447 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6452 /* set frame order for belt animation graphic according to belt direction */
6453 for (i = 0; i < NUM_BELTS; i++)
6457 for (j = 0; j < NUM_BELT_PARTS; j++)
6459 int element = belt_base_active_element[belt_nr] + j;
6460 int graphic_1 = el2img(element);
6461 int graphic_2 = el2panelimg(element);
6463 if (game.belt_dir[i] == MV_LEFT)
6465 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6466 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6470 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6471 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6476 SCAN_PLAYFIELD(x, y)
6478 int element = Feld[x][y];
6480 for (i = 0; i < NUM_BELTS; i++)
6482 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6484 int e_belt_nr = getBeltNrFromBeltElement(element);
6487 if (e_belt_nr == belt_nr)
6489 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6491 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6498 static void ToggleBeltSwitch(int x, int y)
6500 static int belt_base_element[4] =
6502 EL_CONVEYOR_BELT_1_LEFT,
6503 EL_CONVEYOR_BELT_2_LEFT,
6504 EL_CONVEYOR_BELT_3_LEFT,
6505 EL_CONVEYOR_BELT_4_LEFT
6507 static int belt_base_active_element[4] =
6509 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6510 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6511 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6512 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6514 static int belt_base_switch_element[4] =
6516 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6517 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6518 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6519 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6521 static int belt_move_dir[4] =
6529 int element = Feld[x][y];
6530 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6531 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6532 int belt_dir = belt_move_dir[belt_dir_nr];
6535 if (!IS_BELT_SWITCH(element))
6538 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6539 game.belt_dir[belt_nr] = belt_dir;
6541 if (belt_dir_nr == 3)
6544 /* set frame order for belt animation graphic according to belt direction */
6545 for (i = 0; i < NUM_BELT_PARTS; i++)
6547 int element = belt_base_active_element[belt_nr] + i;
6548 int graphic_1 = el2img(element);
6549 int graphic_2 = el2panelimg(element);
6551 if (belt_dir == MV_LEFT)
6553 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6554 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6558 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6559 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6563 SCAN_PLAYFIELD(xx, yy)
6565 int element = Feld[xx][yy];
6567 if (IS_BELT_SWITCH(element))
6569 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6571 if (e_belt_nr == belt_nr)
6573 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6574 TEST_DrawLevelField(xx, yy);
6577 else if (IS_BELT(element) && belt_dir != MV_NONE)
6579 int e_belt_nr = getBeltNrFromBeltElement(element);
6581 if (e_belt_nr == belt_nr)
6583 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6585 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6586 TEST_DrawLevelField(xx, yy);
6589 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6591 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6593 if (e_belt_nr == belt_nr)
6595 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6597 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6598 TEST_DrawLevelField(xx, yy);
6604 static void ToggleSwitchgateSwitch(int x, int y)
6608 game.switchgate_pos = !game.switchgate_pos;
6610 SCAN_PLAYFIELD(xx, yy)
6612 int element = Feld[xx][yy];
6614 #if !USE_BOTH_SWITCHGATE_SWITCHES
6615 if (element == EL_SWITCHGATE_SWITCH_UP ||
6616 element == EL_SWITCHGATE_SWITCH_DOWN)
6618 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6619 TEST_DrawLevelField(xx, yy);
6621 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6622 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6624 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6625 TEST_DrawLevelField(xx, yy);
6628 if (element == EL_SWITCHGATE_SWITCH_UP)
6630 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6631 TEST_DrawLevelField(xx, yy);
6633 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6635 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6636 TEST_DrawLevelField(xx, yy);
6638 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6640 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6641 TEST_DrawLevelField(xx, yy);
6643 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6645 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6646 TEST_DrawLevelField(xx, yy);
6649 else if (element == EL_SWITCHGATE_OPEN ||
6650 element == EL_SWITCHGATE_OPENING)
6652 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6654 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6656 else if (element == EL_SWITCHGATE_CLOSED ||
6657 element == EL_SWITCHGATE_CLOSING)
6659 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6661 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6666 static int getInvisibleActiveFromInvisibleElement(int element)
6668 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6669 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6670 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6674 static int getInvisibleFromInvisibleActiveElement(int element)
6676 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6677 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6678 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6682 static void RedrawAllLightSwitchesAndInvisibleElements()
6686 SCAN_PLAYFIELD(x, y)
6688 int element = Feld[x][y];
6690 if (element == EL_LIGHT_SWITCH &&
6691 game.light_time_left > 0)
6693 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6694 TEST_DrawLevelField(x, y);
6696 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6697 game.light_time_left == 0)
6699 Feld[x][y] = EL_LIGHT_SWITCH;
6700 TEST_DrawLevelField(x, y);
6702 else if (element == EL_EMC_DRIPPER &&
6703 game.light_time_left > 0)
6705 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6706 TEST_DrawLevelField(x, y);
6708 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6709 game.light_time_left == 0)
6711 Feld[x][y] = EL_EMC_DRIPPER;
6712 TEST_DrawLevelField(x, y);
6714 else if (element == EL_INVISIBLE_STEELWALL ||
6715 element == EL_INVISIBLE_WALL ||
6716 element == EL_INVISIBLE_SAND)
6718 if (game.light_time_left > 0)
6719 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6721 TEST_DrawLevelField(x, y);
6723 /* uncrumble neighbour fields, if needed */
6724 if (element == EL_INVISIBLE_SAND)
6725 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6727 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6728 element == EL_INVISIBLE_WALL_ACTIVE ||
6729 element == EL_INVISIBLE_SAND_ACTIVE)
6731 if (game.light_time_left == 0)
6732 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6734 TEST_DrawLevelField(x, y);
6736 /* re-crumble neighbour fields, if needed */
6737 if (element == EL_INVISIBLE_SAND)
6738 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6743 static void RedrawAllInvisibleElementsForLenses()
6747 SCAN_PLAYFIELD(x, y)
6749 int element = Feld[x][y];
6751 if (element == EL_EMC_DRIPPER &&
6752 game.lenses_time_left > 0)
6754 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6755 TEST_DrawLevelField(x, y);
6757 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6758 game.lenses_time_left == 0)
6760 Feld[x][y] = EL_EMC_DRIPPER;
6761 TEST_DrawLevelField(x, y);
6763 else if (element == EL_INVISIBLE_STEELWALL ||
6764 element == EL_INVISIBLE_WALL ||
6765 element == EL_INVISIBLE_SAND)
6767 if (game.lenses_time_left > 0)
6768 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6770 TEST_DrawLevelField(x, y);
6772 /* uncrumble neighbour fields, if needed */
6773 if (element == EL_INVISIBLE_SAND)
6774 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6776 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6777 element == EL_INVISIBLE_WALL_ACTIVE ||
6778 element == EL_INVISIBLE_SAND_ACTIVE)
6780 if (game.lenses_time_left == 0)
6781 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6783 TEST_DrawLevelField(x, y);
6785 /* re-crumble neighbour fields, if needed */
6786 if (element == EL_INVISIBLE_SAND)
6787 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6792 static void RedrawAllInvisibleElementsForMagnifier()
6796 SCAN_PLAYFIELD(x, y)
6798 int element = Feld[x][y];
6800 if (element == EL_EMC_FAKE_GRASS &&
6801 game.magnify_time_left > 0)
6803 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6804 TEST_DrawLevelField(x, y);
6806 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6807 game.magnify_time_left == 0)
6809 Feld[x][y] = EL_EMC_FAKE_GRASS;
6810 TEST_DrawLevelField(x, y);
6812 else if (IS_GATE_GRAY(element) &&
6813 game.magnify_time_left > 0)
6815 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6816 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6817 IS_EM_GATE_GRAY(element) ?
6818 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6819 IS_EMC_GATE_GRAY(element) ?
6820 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6821 IS_DC_GATE_GRAY(element) ?
6822 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6824 TEST_DrawLevelField(x, y);
6826 else if (IS_GATE_GRAY_ACTIVE(element) &&
6827 game.magnify_time_left == 0)
6829 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6830 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6831 IS_EM_GATE_GRAY_ACTIVE(element) ?
6832 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6833 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6834 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6835 IS_DC_GATE_GRAY_ACTIVE(element) ?
6836 EL_DC_GATE_WHITE_GRAY :
6838 TEST_DrawLevelField(x, y);
6843 static void ToggleLightSwitch(int x, int y)
6845 int element = Feld[x][y];
6847 game.light_time_left =
6848 (element == EL_LIGHT_SWITCH ?
6849 level.time_light * FRAMES_PER_SECOND : 0);
6851 RedrawAllLightSwitchesAndInvisibleElements();
6854 static void ActivateTimegateSwitch(int x, int y)
6858 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6860 SCAN_PLAYFIELD(xx, yy)
6862 int element = Feld[xx][yy];
6864 if (element == EL_TIMEGATE_CLOSED ||
6865 element == EL_TIMEGATE_CLOSING)
6867 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6868 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6872 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6874 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6875 TEST_DrawLevelField(xx, yy);
6882 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6883 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6885 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6889 void Impact(int x, int y)
6891 boolean last_line = (y == lev_fieldy - 1);
6892 boolean object_hit = FALSE;
6893 boolean impact = (last_line || object_hit);
6894 int element = Feld[x][y];
6895 int smashed = EL_STEELWALL;
6897 if (!last_line) /* check if element below was hit */
6899 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6902 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6903 MovDir[x][y + 1] != MV_DOWN ||
6904 MovPos[x][y + 1] <= TILEY / 2));
6906 /* do not smash moving elements that left the smashed field in time */
6907 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6908 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6911 #if USE_QUICKSAND_IMPACT_BUGFIX
6912 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6914 RemoveMovingField(x, y + 1);
6915 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6916 Feld[x][y + 2] = EL_ROCK;
6917 TEST_DrawLevelField(x, y + 2);
6922 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6924 RemoveMovingField(x, y + 1);
6925 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6926 Feld[x][y + 2] = EL_ROCK;
6927 TEST_DrawLevelField(x, y + 2);
6934 smashed = MovingOrBlocked2Element(x, y + 1);
6936 impact = (last_line || object_hit);
6939 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6941 SplashAcid(x, y + 1);
6945 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6946 /* only reset graphic animation if graphic really changes after impact */
6948 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6950 ResetGfxAnimation(x, y);
6951 TEST_DrawLevelField(x, y);
6954 if (impact && CAN_EXPLODE_IMPACT(element))
6959 else if (impact && element == EL_PEARL &&
6960 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6962 ResetGfxAnimation(x, y);
6964 Feld[x][y] = EL_PEARL_BREAKING;
6965 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6968 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6970 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6975 if (impact && element == EL_AMOEBA_DROP)
6977 if (object_hit && IS_PLAYER(x, y + 1))
6978 KillPlayerUnlessEnemyProtected(x, y + 1);
6979 else if (object_hit && smashed == EL_PENGUIN)
6983 Feld[x][y] = EL_AMOEBA_GROWING;
6984 Store[x][y] = EL_AMOEBA_WET;
6986 ResetRandomAnimationValue(x, y);
6991 if (object_hit) /* check which object was hit */
6993 if ((CAN_PASS_MAGIC_WALL(element) &&
6994 (smashed == EL_MAGIC_WALL ||
6995 smashed == EL_BD_MAGIC_WALL)) ||
6996 (CAN_PASS_DC_MAGIC_WALL(element) &&
6997 smashed == EL_DC_MAGIC_WALL))
7000 int activated_magic_wall =
7001 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7002 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7003 EL_DC_MAGIC_WALL_ACTIVE);
7005 /* activate magic wall / mill */
7006 SCAN_PLAYFIELD(xx, yy)
7008 if (Feld[xx][yy] == smashed)
7009 Feld[xx][yy] = activated_magic_wall;
7012 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7013 game.magic_wall_active = TRUE;
7015 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7016 SND_MAGIC_WALL_ACTIVATING :
7017 smashed == EL_BD_MAGIC_WALL ?
7018 SND_BD_MAGIC_WALL_ACTIVATING :
7019 SND_DC_MAGIC_WALL_ACTIVATING));
7022 if (IS_PLAYER(x, y + 1))
7024 if (CAN_SMASH_PLAYER(element))
7026 KillPlayerUnlessEnemyProtected(x, y + 1);
7030 else if (smashed == EL_PENGUIN)
7032 if (CAN_SMASH_PLAYER(element))
7038 else if (element == EL_BD_DIAMOND)
7040 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7046 else if (((element == EL_SP_INFOTRON ||
7047 element == EL_SP_ZONK) &&
7048 (smashed == EL_SP_SNIKSNAK ||
7049 smashed == EL_SP_ELECTRON ||
7050 smashed == EL_SP_DISK_ORANGE)) ||
7051 (element == EL_SP_INFOTRON &&
7052 smashed == EL_SP_DISK_YELLOW))
7057 else if (CAN_SMASH_EVERYTHING(element))
7059 if (IS_CLASSIC_ENEMY(smashed) ||
7060 CAN_EXPLODE_SMASHED(smashed))
7065 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7067 if (smashed == EL_LAMP ||
7068 smashed == EL_LAMP_ACTIVE)
7073 else if (smashed == EL_NUT)
7075 Feld[x][y + 1] = EL_NUT_BREAKING;
7076 PlayLevelSound(x, y, SND_NUT_BREAKING);
7077 RaiseScoreElement(EL_NUT);
7080 else if (smashed == EL_PEARL)
7082 ResetGfxAnimation(x, y);
7084 Feld[x][y + 1] = EL_PEARL_BREAKING;
7085 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7088 else if (smashed == EL_DIAMOND)
7090 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7091 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7094 else if (IS_BELT_SWITCH(smashed))
7096 ToggleBeltSwitch(x, y + 1);
7098 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7099 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7100 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7101 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7103 ToggleSwitchgateSwitch(x, y + 1);
7105 else if (smashed == EL_LIGHT_SWITCH ||
7106 smashed == EL_LIGHT_SWITCH_ACTIVE)
7108 ToggleLightSwitch(x, y + 1);
7113 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7116 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7118 CheckElementChangeBySide(x, y + 1, smashed, element,
7119 CE_SWITCHED, CH_SIDE_TOP);
7120 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7126 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7131 /* play sound of magic wall / mill */
7133 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7134 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7135 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7137 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7138 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7139 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7140 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7141 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7142 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7147 /* play sound of object that hits the ground */
7148 if (last_line || object_hit)
7149 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7152 inline static void TurnRoundExt(int x, int y)
7164 { 0, 0 }, { 0, 0 }, { 0, 0 },
7169 int left, right, back;
7173 { MV_DOWN, MV_UP, MV_RIGHT },
7174 { MV_UP, MV_DOWN, MV_LEFT },
7176 { MV_LEFT, MV_RIGHT, MV_DOWN },
7180 { MV_RIGHT, MV_LEFT, MV_UP }
7183 int element = Feld[x][y];
7184 int move_pattern = element_info[element].move_pattern;
7186 int old_move_dir = MovDir[x][y];
7187 int left_dir = turn[old_move_dir].left;
7188 int right_dir = turn[old_move_dir].right;
7189 int back_dir = turn[old_move_dir].back;
7191 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7192 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7193 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7194 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7196 int left_x = x + left_dx, left_y = y + left_dy;
7197 int right_x = x + right_dx, right_y = y + right_dy;
7198 int move_x = x + move_dx, move_y = y + move_dy;
7202 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7204 TestIfBadThingTouchesOtherBadThing(x, y);
7206 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7207 MovDir[x][y] = right_dir;
7208 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7209 MovDir[x][y] = left_dir;
7211 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7213 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7216 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7218 TestIfBadThingTouchesOtherBadThing(x, y);
7220 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7221 MovDir[x][y] = left_dir;
7222 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7223 MovDir[x][y] = right_dir;
7225 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7227 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7230 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7232 TestIfBadThingTouchesOtherBadThing(x, y);
7234 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7235 MovDir[x][y] = left_dir;
7236 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7237 MovDir[x][y] = right_dir;
7239 if (MovDir[x][y] != old_move_dir)
7242 else if (element == EL_YAMYAM)
7244 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7245 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7247 if (can_turn_left && can_turn_right)
7248 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7249 else if (can_turn_left)
7250 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7251 else if (can_turn_right)
7252 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7254 MovDir[x][y] = back_dir;
7256 MovDelay[x][y] = 16 + 16 * RND(3);
7258 else if (element == EL_DARK_YAMYAM)
7260 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7262 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7265 if (can_turn_left && can_turn_right)
7266 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7267 else if (can_turn_left)
7268 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7269 else if (can_turn_right)
7270 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7272 MovDir[x][y] = back_dir;
7274 MovDelay[x][y] = 16 + 16 * RND(3);
7276 else if (element == EL_PACMAN)
7278 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7279 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7281 if (can_turn_left && can_turn_right)
7282 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283 else if (can_turn_left)
7284 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285 else if (can_turn_right)
7286 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7288 MovDir[x][y] = back_dir;
7290 MovDelay[x][y] = 6 + RND(40);
7292 else if (element == EL_PIG)
7294 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7295 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7296 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7297 boolean should_turn_left, should_turn_right, should_move_on;
7299 int rnd = RND(rnd_value);
7301 should_turn_left = (can_turn_left &&
7303 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7304 y + back_dy + left_dy)));
7305 should_turn_right = (can_turn_right &&
7307 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7308 y + back_dy + right_dy)));
7309 should_move_on = (can_move_on &&
7312 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7313 y + move_dy + left_dy) ||
7314 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7315 y + move_dy + right_dy)));
7317 if (should_turn_left || should_turn_right || should_move_on)
7319 if (should_turn_left && should_turn_right && should_move_on)
7320 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7321 rnd < 2 * rnd_value / 3 ? right_dir :
7323 else if (should_turn_left && should_turn_right)
7324 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7325 else if (should_turn_left && should_move_on)
7326 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7327 else if (should_turn_right && should_move_on)
7328 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7329 else if (should_turn_left)
7330 MovDir[x][y] = left_dir;
7331 else if (should_turn_right)
7332 MovDir[x][y] = right_dir;
7333 else if (should_move_on)
7334 MovDir[x][y] = old_move_dir;
7336 else if (can_move_on && rnd > rnd_value / 8)
7337 MovDir[x][y] = old_move_dir;
7338 else if (can_turn_left && can_turn_right)
7339 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7340 else if (can_turn_left && rnd > rnd_value / 8)
7341 MovDir[x][y] = left_dir;
7342 else if (can_turn_right && rnd > rnd_value/8)
7343 MovDir[x][y] = right_dir;
7345 MovDir[x][y] = back_dir;
7347 xx = x + move_xy[MovDir[x][y]].dx;
7348 yy = y + move_xy[MovDir[x][y]].dy;
7350 if (!IN_LEV_FIELD(xx, yy) ||
7351 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7352 MovDir[x][y] = old_move_dir;
7356 else if (element == EL_DRAGON)
7358 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7359 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7360 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7362 int rnd = RND(rnd_value);
7364 if (can_move_on && rnd > rnd_value / 8)
7365 MovDir[x][y] = old_move_dir;
7366 else if (can_turn_left && can_turn_right)
7367 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7368 else if (can_turn_left && rnd > rnd_value / 8)
7369 MovDir[x][y] = left_dir;
7370 else if (can_turn_right && rnd > rnd_value / 8)
7371 MovDir[x][y] = right_dir;
7373 MovDir[x][y] = back_dir;
7375 xx = x + move_xy[MovDir[x][y]].dx;
7376 yy = y + move_xy[MovDir[x][y]].dy;
7378 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7379 MovDir[x][y] = old_move_dir;
7383 else if (element == EL_MOLE)
7385 boolean can_move_on =
7386 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7387 IS_AMOEBOID(Feld[move_x][move_y]) ||
7388 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7391 boolean can_turn_left =
7392 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7393 IS_AMOEBOID(Feld[left_x][left_y])));
7395 boolean can_turn_right =
7396 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7397 IS_AMOEBOID(Feld[right_x][right_y])));
7399 if (can_turn_left && can_turn_right)
7400 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7401 else if (can_turn_left)
7402 MovDir[x][y] = left_dir;
7404 MovDir[x][y] = right_dir;
7407 if (MovDir[x][y] != old_move_dir)
7410 else if (element == EL_BALLOON)
7412 MovDir[x][y] = game.wind_direction;
7415 else if (element == EL_SPRING)
7417 #if USE_NEW_SPRING_BUMPER
7418 if (MovDir[x][y] & MV_HORIZONTAL)
7420 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7421 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7423 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7424 ResetGfxAnimation(move_x, move_y);
7425 TEST_DrawLevelField(move_x, move_y);
7427 MovDir[x][y] = back_dir;
7429 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7430 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7431 MovDir[x][y] = MV_NONE;
7434 if (MovDir[x][y] & MV_HORIZONTAL &&
7435 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7436 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7437 MovDir[x][y] = MV_NONE;
7442 else if (element == EL_ROBOT ||
7443 element == EL_SATELLITE ||
7444 element == EL_PENGUIN ||
7445 element == EL_EMC_ANDROID)
7447 int attr_x = -1, attr_y = -1;
7458 for (i = 0; i < MAX_PLAYERS; i++)
7460 struct PlayerInfo *player = &stored_player[i];
7461 int jx = player->jx, jy = player->jy;
7463 if (!player->active)
7467 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7475 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7476 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7477 game.engine_version < VERSION_IDENT(3,1,0,0)))
7483 if (element == EL_PENGUIN)
7486 static int xy[4][2] =
7494 for (i = 0; i < NUM_DIRECTIONS; i++)
7496 int ex = x + xy[i][0];
7497 int ey = y + xy[i][1];
7499 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7500 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7501 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7502 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7511 MovDir[x][y] = MV_NONE;
7513 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7514 else if (attr_x > x)
7515 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7517 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7518 else if (attr_y > y)
7519 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7521 if (element == EL_ROBOT)
7525 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7527 Moving2Blocked(x, y, &newx, &newy);
7529 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7530 MovDelay[x][y] = 8 + 8 * !RND(3);
7532 MovDelay[x][y] = 16;
7534 else if (element == EL_PENGUIN)
7540 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7542 boolean first_horiz = RND(2);
7543 int new_move_dir = MovDir[x][y];
7546 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7547 Moving2Blocked(x, y, &newx, &newy);
7549 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7553 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7554 Moving2Blocked(x, y, &newx, &newy);
7556 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7559 MovDir[x][y] = old_move_dir;
7563 else if (element == EL_SATELLITE)
7569 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7571 boolean first_horiz = RND(2);
7572 int new_move_dir = MovDir[x][y];
7575 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7576 Moving2Blocked(x, y, &newx, &newy);
7578 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7582 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7583 Moving2Blocked(x, y, &newx, &newy);
7585 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7588 MovDir[x][y] = old_move_dir;
7592 else if (element == EL_EMC_ANDROID)
7594 static int check_pos[16] =
7596 -1, /* 0 => (invalid) */
7597 7, /* 1 => MV_LEFT */
7598 3, /* 2 => MV_RIGHT */
7599 -1, /* 3 => (invalid) */
7601 0, /* 5 => MV_LEFT | MV_UP */
7602 2, /* 6 => MV_RIGHT | MV_UP */
7603 -1, /* 7 => (invalid) */
7604 5, /* 8 => MV_DOWN */
7605 6, /* 9 => MV_LEFT | MV_DOWN */
7606 4, /* 10 => MV_RIGHT | MV_DOWN */
7607 -1, /* 11 => (invalid) */
7608 -1, /* 12 => (invalid) */
7609 -1, /* 13 => (invalid) */
7610 -1, /* 14 => (invalid) */
7611 -1, /* 15 => (invalid) */
7619 { -1, -1, MV_LEFT | MV_UP },
7621 { +1, -1, MV_RIGHT | MV_UP },
7622 { +1, 0, MV_RIGHT },
7623 { +1, +1, MV_RIGHT | MV_DOWN },
7625 { -1, +1, MV_LEFT | MV_DOWN },
7628 int start_pos, check_order;
7629 boolean can_clone = FALSE;
7632 /* check if there is any free field around current position */
7633 for (i = 0; i < 8; i++)
7635 int newx = x + check_xy[i].dx;
7636 int newy = y + check_xy[i].dy;
7638 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7646 if (can_clone) /* randomly find an element to clone */
7650 start_pos = check_pos[RND(8)];
7651 check_order = (RND(2) ? -1 : +1);
7653 for (i = 0; i < 8; i++)
7655 int pos_raw = start_pos + i * check_order;
7656 int pos = (pos_raw + 8) % 8;
7657 int newx = x + check_xy[pos].dx;
7658 int newy = y + check_xy[pos].dy;
7660 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7662 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7663 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7665 Store[x][y] = Feld[newx][newy];
7674 if (can_clone) /* randomly find a direction to move */
7678 start_pos = check_pos[RND(8)];
7679 check_order = (RND(2) ? -1 : +1);
7681 for (i = 0; i < 8; i++)
7683 int pos_raw = start_pos + i * check_order;
7684 int pos = (pos_raw + 8) % 8;
7685 int newx = x + check_xy[pos].dx;
7686 int newy = y + check_xy[pos].dy;
7687 int new_move_dir = check_xy[pos].dir;
7689 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7691 MovDir[x][y] = new_move_dir;
7692 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7701 if (can_clone) /* cloning and moving successful */
7704 /* cannot clone -- try to move towards player */
7706 start_pos = check_pos[MovDir[x][y] & 0x0f];
7707 check_order = (RND(2) ? -1 : +1);
7709 for (i = 0; i < 3; i++)
7711 /* first check start_pos, then previous/next or (next/previous) pos */
7712 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7713 int pos = (pos_raw + 8) % 8;
7714 int newx = x + check_xy[pos].dx;
7715 int newy = y + check_xy[pos].dy;
7716 int new_move_dir = check_xy[pos].dir;
7718 if (IS_PLAYER(newx, newy))
7721 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7723 MovDir[x][y] = new_move_dir;
7724 MovDelay[x][y] = level.android_move_time * 8 + 1;
7731 else if (move_pattern == MV_TURNING_LEFT ||
7732 move_pattern == MV_TURNING_RIGHT ||
7733 move_pattern == MV_TURNING_LEFT_RIGHT ||
7734 move_pattern == MV_TURNING_RIGHT_LEFT ||
7735 move_pattern == MV_TURNING_RANDOM ||
7736 move_pattern == MV_ALL_DIRECTIONS)
7738 boolean can_turn_left =
7739 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7740 boolean can_turn_right =
7741 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7743 if (element_info[element].move_stepsize == 0) /* "not moving" */
7746 if (move_pattern == MV_TURNING_LEFT)
7747 MovDir[x][y] = left_dir;
7748 else if (move_pattern == MV_TURNING_RIGHT)
7749 MovDir[x][y] = right_dir;
7750 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7751 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7752 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7753 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7754 else if (move_pattern == MV_TURNING_RANDOM)
7755 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7756 can_turn_right && !can_turn_left ? right_dir :
7757 RND(2) ? left_dir : right_dir);
7758 else if (can_turn_left && can_turn_right)
7759 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7760 else if (can_turn_left)
7761 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7762 else if (can_turn_right)
7763 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7765 MovDir[x][y] = back_dir;
7767 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7769 else if (move_pattern == MV_HORIZONTAL ||
7770 move_pattern == MV_VERTICAL)
7772 if (move_pattern & old_move_dir)
7773 MovDir[x][y] = back_dir;
7774 else if (move_pattern == MV_HORIZONTAL)
7775 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7776 else if (move_pattern == MV_VERTICAL)
7777 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7779 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7781 else if (move_pattern & MV_ANY_DIRECTION)
7783 MovDir[x][y] = move_pattern;
7784 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7786 else if (move_pattern & MV_WIND_DIRECTION)
7788 MovDir[x][y] = game.wind_direction;
7789 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7791 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7793 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7794 MovDir[x][y] = left_dir;
7795 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7796 MovDir[x][y] = right_dir;
7798 if (MovDir[x][y] != old_move_dir)
7799 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7801 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7803 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7804 MovDir[x][y] = right_dir;
7805 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7806 MovDir[x][y] = left_dir;
7808 if (MovDir[x][y] != old_move_dir)
7809 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7811 else if (move_pattern == MV_TOWARDS_PLAYER ||
7812 move_pattern == MV_AWAY_FROM_PLAYER)
7814 int attr_x = -1, attr_y = -1;
7816 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7827 for (i = 0; i < MAX_PLAYERS; i++)
7829 struct PlayerInfo *player = &stored_player[i];
7830 int jx = player->jx, jy = player->jy;
7832 if (!player->active)
7836 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7844 MovDir[x][y] = MV_NONE;
7846 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7847 else if (attr_x > x)
7848 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7850 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7851 else if (attr_y > y)
7852 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7854 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7856 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7858 boolean first_horiz = RND(2);
7859 int new_move_dir = MovDir[x][y];
7861 if (element_info[element].move_stepsize == 0) /* "not moving" */
7863 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7864 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7870 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7871 Moving2Blocked(x, y, &newx, &newy);
7873 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7877 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7878 Moving2Blocked(x, y, &newx, &newy);
7880 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7883 MovDir[x][y] = old_move_dir;
7886 else if (move_pattern == MV_WHEN_PUSHED ||
7887 move_pattern == MV_WHEN_DROPPED)
7889 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7890 MovDir[x][y] = MV_NONE;
7894 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7896 static int test_xy[7][2] =
7906 static int test_dir[7] =
7916 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7917 int move_preference = -1000000; /* start with very low preference */
7918 int new_move_dir = MV_NONE;
7919 int start_test = RND(4);
7922 for (i = 0; i < NUM_DIRECTIONS; i++)
7924 int move_dir = test_dir[start_test + i];
7925 int move_dir_preference;
7927 xx = x + test_xy[start_test + i][0];
7928 yy = y + test_xy[start_test + i][1];
7930 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7931 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7933 new_move_dir = move_dir;
7938 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7941 move_dir_preference = -1 * RunnerVisit[xx][yy];
7942 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7943 move_dir_preference = PlayerVisit[xx][yy];
7945 if (move_dir_preference > move_preference)
7947 /* prefer field that has not been visited for the longest time */
7948 move_preference = move_dir_preference;
7949 new_move_dir = move_dir;
7951 else if (move_dir_preference == move_preference &&
7952 move_dir == old_move_dir)
7954 /* prefer last direction when all directions are preferred equally */
7955 move_preference = move_dir_preference;
7956 new_move_dir = move_dir;
7960 MovDir[x][y] = new_move_dir;
7961 if (old_move_dir != new_move_dir)
7962 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7966 static void TurnRound(int x, int y)
7968 int direction = MovDir[x][y];
7972 GfxDir[x][y] = MovDir[x][y];
7974 if (direction != MovDir[x][y])
7978 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7980 ResetGfxFrame(x, y, FALSE);
7983 static boolean JustBeingPushed(int x, int y)
7987 for (i = 0; i < MAX_PLAYERS; i++)
7989 struct PlayerInfo *player = &stored_player[i];
7991 if (player->active && player->is_pushing && player->MovPos)
7993 int next_jx = player->jx + (player->jx - player->last_jx);
7994 int next_jy = player->jy + (player->jy - player->last_jy);
7996 if (x == next_jx && y == next_jy)
8004 void StartMoving(int x, int y)
8006 boolean started_moving = FALSE; /* some elements can fall _and_ move */
8007 int element = Feld[x][y];
8012 if (MovDelay[x][y] == 0)
8013 GfxAction[x][y] = ACTION_DEFAULT;
8015 if (CAN_FALL(element) && y < lev_fieldy - 1)
8017 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
8018 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8019 if (JustBeingPushed(x, y))
8022 if (element == EL_QUICKSAND_FULL)
8024 if (IS_FREE(x, y + 1))
8026 InitMovingField(x, y, MV_DOWN);
8027 started_moving = TRUE;
8029 Feld[x][y] = EL_QUICKSAND_EMPTYING;
8030 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8031 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8032 Store[x][y] = EL_ROCK;
8034 Store[x][y] = EL_ROCK;
8037 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8039 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8041 if (!MovDelay[x][y])
8043 MovDelay[x][y] = TILEY + 1;
8045 ResetGfxAnimation(x, y);
8046 ResetGfxAnimation(x, y + 1);
8051 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8052 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8059 Feld[x][y] = EL_QUICKSAND_EMPTY;
8060 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8061 Store[x][y + 1] = Store[x][y];
8064 PlayLevelSoundAction(x, y, ACTION_FILLING);
8066 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8068 if (!MovDelay[x][y])
8070 MovDelay[x][y] = TILEY + 1;
8072 ResetGfxAnimation(x, y);
8073 ResetGfxAnimation(x, y + 1);
8078 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8079 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8086 Feld[x][y] = EL_QUICKSAND_EMPTY;
8087 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088 Store[x][y + 1] = Store[x][y];
8091 PlayLevelSoundAction(x, y, ACTION_FILLING);
8094 else if (element == EL_QUICKSAND_FAST_FULL)
8096 if (IS_FREE(x, y + 1))
8098 InitMovingField(x, y, MV_DOWN);
8099 started_moving = TRUE;
8101 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8102 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8103 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8104 Store[x][y] = EL_ROCK;
8106 Store[x][y] = EL_ROCK;
8109 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8111 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8113 if (!MovDelay[x][y])
8115 MovDelay[x][y] = TILEY + 1;
8117 ResetGfxAnimation(x, y);
8118 ResetGfxAnimation(x, y + 1);
8123 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8124 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8131 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8132 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8133 Store[x][y + 1] = Store[x][y];
8136 PlayLevelSoundAction(x, y, ACTION_FILLING);
8138 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8140 if (!MovDelay[x][y])
8142 MovDelay[x][y] = TILEY + 1;
8144 ResetGfxAnimation(x, y);
8145 ResetGfxAnimation(x, y + 1);
8150 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8151 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8158 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8159 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8160 Store[x][y + 1] = Store[x][y];
8163 PlayLevelSoundAction(x, y, ACTION_FILLING);
8166 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8167 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8169 InitMovingField(x, y, MV_DOWN);
8170 started_moving = TRUE;
8172 Feld[x][y] = EL_QUICKSAND_FILLING;
8173 Store[x][y] = element;
8175 PlayLevelSoundAction(x, y, ACTION_FILLING);
8177 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8178 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8180 InitMovingField(x, y, MV_DOWN);
8181 started_moving = TRUE;
8183 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8184 Store[x][y] = element;
8186 PlayLevelSoundAction(x, y, ACTION_FILLING);
8188 else if (element == EL_MAGIC_WALL_FULL)
8190 if (IS_FREE(x, y + 1))
8192 InitMovingField(x, y, MV_DOWN);
8193 started_moving = TRUE;
8195 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8196 Store[x][y] = EL_CHANGED(Store[x][y]);
8198 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8200 if (!MovDelay[x][y])
8201 MovDelay[x][y] = TILEY / 4 + 1;
8210 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8211 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8212 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8216 else if (element == EL_BD_MAGIC_WALL_FULL)
8218 if (IS_FREE(x, y + 1))
8220 InitMovingField(x, y, MV_DOWN);
8221 started_moving = TRUE;
8223 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8224 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8226 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8228 if (!MovDelay[x][y])
8229 MovDelay[x][y] = TILEY / 4 + 1;
8238 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8239 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8240 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8244 else if (element == EL_DC_MAGIC_WALL_FULL)
8246 if (IS_FREE(x, y + 1))
8248 InitMovingField(x, y, MV_DOWN);
8249 started_moving = TRUE;
8251 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8252 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8254 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8256 if (!MovDelay[x][y])
8257 MovDelay[x][y] = TILEY / 4 + 1;
8266 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8267 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8268 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8272 else if ((CAN_PASS_MAGIC_WALL(element) &&
8273 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8274 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8275 (CAN_PASS_DC_MAGIC_WALL(element) &&
8276 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8279 InitMovingField(x, y, MV_DOWN);
8280 started_moving = TRUE;
8283 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8284 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8285 EL_DC_MAGIC_WALL_FILLING);
8286 Store[x][y] = element;
8288 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8290 SplashAcid(x, y + 1);
8292 InitMovingField(x, y, MV_DOWN);
8293 started_moving = TRUE;
8295 Store[x][y] = EL_ACID;
8298 #if USE_FIX_IMPACT_COLLISION
8299 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8300 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8302 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8303 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8305 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8306 CAN_FALL(element) && WasJustFalling[x][y] &&
8307 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8309 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8310 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8311 (Feld[x][y + 1] == EL_BLOCKED)))
8313 /* this is needed for a special case not covered by calling "Impact()"
8314 from "ContinueMoving()": if an element moves to a tile directly below
8315 another element which was just falling on that tile (which was empty
8316 in the previous frame), the falling element above would just stop
8317 instead of smashing the element below (in previous version, the above
8318 element was just checked for "moving" instead of "falling", resulting
8319 in incorrect smashes caused by horizontal movement of the above
8320 element; also, the case of the player being the element to smash was
8321 simply not covered here... :-/ ) */
8323 CheckCollision[x][y] = 0;
8324 CheckImpact[x][y] = 0;
8328 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8330 if (MovDir[x][y] == MV_NONE)
8332 InitMovingField(x, y, MV_DOWN);
8333 started_moving = TRUE;
8336 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8338 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8339 MovDir[x][y] = MV_DOWN;
8341 InitMovingField(x, y, MV_DOWN);
8342 started_moving = TRUE;
8344 else if (element == EL_AMOEBA_DROP)
8346 Feld[x][y] = EL_AMOEBA_GROWING;
8347 Store[x][y] = EL_AMOEBA_WET;
8349 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8350 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8351 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8352 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8354 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8355 (IS_FREE(x - 1, y + 1) ||
8356 Feld[x - 1][y + 1] == EL_ACID));
8357 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8358 (IS_FREE(x + 1, y + 1) ||
8359 Feld[x + 1][y + 1] == EL_ACID));
8360 boolean can_fall_any = (can_fall_left || can_fall_right);
8361 boolean can_fall_both = (can_fall_left && can_fall_right);
8362 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8364 #if USE_NEW_ALL_SLIPPERY
8365 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8367 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8368 can_fall_right = FALSE;
8369 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8370 can_fall_left = FALSE;
8371 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8372 can_fall_right = FALSE;
8373 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8374 can_fall_left = FALSE;
8376 can_fall_any = (can_fall_left || can_fall_right);
8377 can_fall_both = FALSE;
8380 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8382 if (slippery_type == SLIPPERY_ONLY_LEFT)
8383 can_fall_right = FALSE;
8384 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8385 can_fall_left = FALSE;
8386 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8387 can_fall_right = FALSE;
8388 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8389 can_fall_left = FALSE;
8391 can_fall_any = (can_fall_left || can_fall_right);
8392 can_fall_both = (can_fall_left && can_fall_right);
8396 #if USE_NEW_ALL_SLIPPERY
8398 #if USE_NEW_SP_SLIPPERY
8399 /* !!! better use the same properties as for custom elements here !!! */
8400 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8401 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8403 can_fall_right = FALSE; /* slip down on left side */
8404 can_fall_both = FALSE;
8409 #if USE_NEW_ALL_SLIPPERY
8412 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8413 can_fall_right = FALSE; /* slip down on left side */
8415 can_fall_left = !(can_fall_right = RND(2));
8417 can_fall_both = FALSE;
8422 if (game.emulation == EMU_BOULDERDASH ||
8423 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8424 can_fall_right = FALSE; /* slip down on left side */
8426 can_fall_left = !(can_fall_right = RND(2));
8428 can_fall_both = FALSE;
8434 /* if not determined otherwise, prefer left side for slipping down */
8435 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8436 started_moving = TRUE;
8440 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8442 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8445 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8446 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8447 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8448 int belt_dir = game.belt_dir[belt_nr];
8450 if ((belt_dir == MV_LEFT && left_is_free) ||
8451 (belt_dir == MV_RIGHT && right_is_free))
8453 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8455 InitMovingField(x, y, belt_dir);
8456 started_moving = TRUE;
8458 Pushed[x][y] = TRUE;
8459 Pushed[nextx][y] = TRUE;
8461 GfxAction[x][y] = ACTION_DEFAULT;
8465 MovDir[x][y] = 0; /* if element was moving, stop it */
8470 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8472 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8474 if (CAN_MOVE(element) && !started_moving)
8477 int move_pattern = element_info[element].move_pattern;
8482 if (MovDir[x][y] == MV_NONE)
8484 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8485 x, y, element, element_info[element].token_name);
8486 printf("StartMoving(): This should never happen!\n");
8491 Moving2Blocked(x, y, &newx, &newy);
8493 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8496 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8497 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8499 WasJustMoving[x][y] = 0;
8500 CheckCollision[x][y] = 0;
8502 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8504 if (Feld[x][y] != element) /* element has changed */
8508 if (!MovDelay[x][y]) /* start new movement phase */
8510 /* all objects that can change their move direction after each step
8511 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8513 if (element != EL_YAMYAM &&
8514 element != EL_DARK_YAMYAM &&
8515 element != EL_PACMAN &&
8516 !(move_pattern & MV_ANY_DIRECTION) &&
8517 move_pattern != MV_TURNING_LEFT &&
8518 move_pattern != MV_TURNING_RIGHT &&
8519 move_pattern != MV_TURNING_LEFT_RIGHT &&
8520 move_pattern != MV_TURNING_RIGHT_LEFT &&
8521 move_pattern != MV_TURNING_RANDOM)
8525 if (MovDelay[x][y] && (element == EL_BUG ||
8526 element == EL_SPACESHIP ||
8527 element == EL_SP_SNIKSNAK ||
8528 element == EL_SP_ELECTRON ||
8529 element == EL_MOLE))
8530 TEST_DrawLevelField(x, y);
8534 if (MovDelay[x][y]) /* wait some time before next movement */
8538 if (element == EL_ROBOT ||
8539 element == EL_YAMYAM ||
8540 element == EL_DARK_YAMYAM)
8542 DrawLevelElementAnimationIfNeeded(x, y, element);
8543 PlayLevelSoundAction(x, y, ACTION_WAITING);
8545 else if (element == EL_SP_ELECTRON)
8546 DrawLevelElementAnimationIfNeeded(x, y, element);
8547 else if (element == EL_DRAGON)
8550 int dir = MovDir[x][y];
8551 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8552 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8553 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8554 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8555 dir == MV_UP ? IMG_FLAMES_1_UP :
8556 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8557 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8559 GfxAction[x][y] = ACTION_ATTACKING;
8561 if (IS_PLAYER(x, y))
8562 DrawPlayerField(x, y);
8564 TEST_DrawLevelField(x, y);
8566 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8568 for (i = 1; i <= 3; i++)
8570 int xx = x + i * dx;
8571 int yy = y + i * dy;
8572 int sx = SCREENX(xx);
8573 int sy = SCREENY(yy);
8574 int flame_graphic = graphic + (i - 1);
8576 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8581 int flamed = MovingOrBlocked2Element(xx, yy);
8585 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8587 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8588 RemoveMovingField(xx, yy);
8590 RemoveField(xx, yy);
8592 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8595 RemoveMovingField(xx, yy);
8598 ChangeDelay[xx][yy] = 0;
8600 Feld[xx][yy] = EL_FLAMES;
8602 if (IN_SCR_FIELD(sx, sy))
8604 TEST_DrawLevelFieldCrumbled(xx, yy);
8605 DrawGraphic(sx, sy, flame_graphic, frame);
8610 if (Feld[xx][yy] == EL_FLAMES)
8611 Feld[xx][yy] = EL_EMPTY;
8612 TEST_DrawLevelField(xx, yy);
8617 if (MovDelay[x][y]) /* element still has to wait some time */
8619 PlayLevelSoundAction(x, y, ACTION_WAITING);
8625 /* now make next step */
8627 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8629 if (DONT_COLLIDE_WITH(element) &&
8630 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8631 !PLAYER_ENEMY_PROTECTED(newx, newy))
8633 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8638 else if (CAN_MOVE_INTO_ACID(element) &&
8639 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8640 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8641 (MovDir[x][y] == MV_DOWN ||
8642 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8644 SplashAcid(newx, newy);
8645 Store[x][y] = EL_ACID;
8647 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8649 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8650 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8651 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8652 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8655 TEST_DrawLevelField(x, y);
8657 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8658 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8659 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8661 local_player->friends_still_needed--;
8662 if (!local_player->friends_still_needed &&
8663 !local_player->GameOver && AllPlayersGone)
8664 PlayerWins(local_player);
8668 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8670 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8671 TEST_DrawLevelField(newx, newy);
8673 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8675 else if (!IS_FREE(newx, newy))
8677 GfxAction[x][y] = ACTION_WAITING;
8679 if (IS_PLAYER(x, y))
8680 DrawPlayerField(x, y);
8682 TEST_DrawLevelField(x, y);
8687 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8689 if (IS_FOOD_PIG(Feld[newx][newy]))
8691 if (IS_MOVING(newx, newy))
8692 RemoveMovingField(newx, newy);
8695 Feld[newx][newy] = EL_EMPTY;
8696 TEST_DrawLevelField(newx, newy);
8699 PlayLevelSound(x, y, SND_PIG_DIGGING);
8701 else if (!IS_FREE(newx, newy))
8703 if (IS_PLAYER(x, y))
8704 DrawPlayerField(x, y);
8706 TEST_DrawLevelField(x, y);
8711 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8713 if (Store[x][y] != EL_EMPTY)
8715 boolean can_clone = FALSE;
8718 /* check if element to clone is still there */
8719 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8721 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8729 /* cannot clone or target field not free anymore -- do not clone */
8730 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8731 Store[x][y] = EL_EMPTY;
8734 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8736 if (IS_MV_DIAGONAL(MovDir[x][y]))
8738 int diagonal_move_dir = MovDir[x][y];
8739 int stored = Store[x][y];
8740 int change_delay = 8;
8743 /* android is moving diagonally */
8745 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8747 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8748 GfxElement[x][y] = EL_EMC_ANDROID;
8749 GfxAction[x][y] = ACTION_SHRINKING;
8750 GfxDir[x][y] = diagonal_move_dir;
8751 ChangeDelay[x][y] = change_delay;
8753 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8756 DrawLevelGraphicAnimation(x, y, graphic);
8757 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8759 if (Feld[newx][newy] == EL_ACID)
8761 SplashAcid(newx, newy);
8766 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8768 Store[newx][newy] = EL_EMC_ANDROID;
8769 GfxElement[newx][newy] = EL_EMC_ANDROID;
8770 GfxAction[newx][newy] = ACTION_GROWING;
8771 GfxDir[newx][newy] = diagonal_move_dir;
8772 ChangeDelay[newx][newy] = change_delay;
8774 graphic = el_act_dir2img(GfxElement[newx][newy],
8775 GfxAction[newx][newy], GfxDir[newx][newy]);
8777 DrawLevelGraphicAnimation(newx, newy, graphic);
8778 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8784 Feld[newx][newy] = EL_EMPTY;
8785 TEST_DrawLevelField(newx, newy);
8787 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8790 else if (!IS_FREE(newx, newy))
8793 if (IS_PLAYER(x, y))
8794 DrawPlayerField(x, y);
8796 TEST_DrawLevelField(x, y);
8802 else if (IS_CUSTOM_ELEMENT(element) &&
8803 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8806 if (!DigFieldByCE(newx, newy, element))
8809 int new_element = Feld[newx][newy];
8811 if (!IS_FREE(newx, newy))
8813 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8814 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8817 /* no element can dig solid indestructible elements */
8818 if (IS_INDESTRUCTIBLE(new_element) &&
8819 !IS_DIGGABLE(new_element) &&
8820 !IS_COLLECTIBLE(new_element))
8823 if (AmoebaNr[newx][newy] &&
8824 (new_element == EL_AMOEBA_FULL ||
8825 new_element == EL_BD_AMOEBA ||
8826 new_element == EL_AMOEBA_GROWING))
8828 AmoebaCnt[AmoebaNr[newx][newy]]--;
8829 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8832 if (IS_MOVING(newx, newy))
8833 RemoveMovingField(newx, newy);
8836 RemoveField(newx, newy);
8837 TEST_DrawLevelField(newx, newy);
8840 /* if digged element was about to explode, prevent the explosion */
8841 ExplodeField[newx][newy] = EX_TYPE_NONE;
8843 PlayLevelSoundAction(x, y, action);
8846 Store[newx][newy] = EL_EMPTY;
8849 /* this makes it possible to leave the removed element again */
8850 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8851 Store[newx][newy] = new_element;
8853 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8855 int move_leave_element = element_info[element].move_leave_element;
8857 /* this makes it possible to leave the removed element again */
8858 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8859 new_element : move_leave_element);
8865 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8867 RunnerVisit[x][y] = FrameCounter;
8868 PlayerVisit[x][y] /= 8; /* expire player visit path */
8871 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8873 if (!IS_FREE(newx, newy))
8875 if (IS_PLAYER(x, y))
8876 DrawPlayerField(x, y);
8878 TEST_DrawLevelField(x, y);
8884 boolean wanna_flame = !RND(10);
8885 int dx = newx - x, dy = newy - y;
8886 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8887 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8888 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8889 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8890 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8891 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8894 IS_CLASSIC_ENEMY(element1) ||
8895 IS_CLASSIC_ENEMY(element2)) &&
8896 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8897 element1 != EL_FLAMES && element2 != EL_FLAMES)
8899 ResetGfxAnimation(x, y);
8900 GfxAction[x][y] = ACTION_ATTACKING;
8902 if (IS_PLAYER(x, y))
8903 DrawPlayerField(x, y);
8905 TEST_DrawLevelField(x, y);
8907 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8909 MovDelay[x][y] = 50;
8913 RemoveField(newx, newy);
8915 Feld[newx][newy] = EL_FLAMES;
8916 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8919 RemoveField(newx1, newy1);
8921 Feld[newx1][newy1] = EL_FLAMES;
8923 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8926 RemoveField(newx2, newy2);
8928 Feld[newx2][newy2] = EL_FLAMES;
8935 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8936 Feld[newx][newy] == EL_DIAMOND)
8938 if (IS_MOVING(newx, newy))
8939 RemoveMovingField(newx, newy);
8942 Feld[newx][newy] = EL_EMPTY;
8943 TEST_DrawLevelField(newx, newy);
8946 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8948 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8949 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8951 if (AmoebaNr[newx][newy])
8953 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8954 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8955 Feld[newx][newy] == EL_BD_AMOEBA)
8956 AmoebaCnt[AmoebaNr[newx][newy]]--;
8961 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8963 RemoveMovingField(newx, newy);
8966 if (IS_MOVING(newx, newy))
8968 RemoveMovingField(newx, newy);
8973 Feld[newx][newy] = EL_EMPTY;
8974 TEST_DrawLevelField(newx, newy);
8977 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8979 else if ((element == EL_PACMAN || element == EL_MOLE)
8980 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8982 if (AmoebaNr[newx][newy])
8984 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8985 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8986 Feld[newx][newy] == EL_BD_AMOEBA)
8987 AmoebaCnt[AmoebaNr[newx][newy]]--;
8990 if (element == EL_MOLE)
8992 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8993 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8995 ResetGfxAnimation(x, y);
8996 GfxAction[x][y] = ACTION_DIGGING;
8997 TEST_DrawLevelField(x, y);
8999 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
9001 return; /* wait for shrinking amoeba */
9003 else /* element == EL_PACMAN */
9005 Feld[newx][newy] = EL_EMPTY;
9006 TEST_DrawLevelField(newx, newy);
9007 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
9010 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
9011 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
9012 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
9014 /* wait for shrinking amoeba to completely disappear */
9017 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
9019 /* object was running against a wall */
9024 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
9025 if (move_pattern & MV_ANY_DIRECTION &&
9026 move_pattern == MovDir[x][y])
9028 int blocking_element =
9029 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
9031 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
9034 element = Feld[x][y]; /* element might have changed */
9038 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
9039 DrawLevelElementAnimation(x, y, element);
9041 if (DONT_TOUCH(element))
9042 TestIfBadThingTouchesPlayer(x, y);
9047 InitMovingField(x, y, MovDir[x][y]);
9049 PlayLevelSoundAction(x, y, ACTION_MOVING);
9053 ContinueMoving(x, y);
9056 void ContinueMoving(int x, int y)
9058 int element = Feld[x][y];
9059 struct ElementInfo *ei = &element_info[element];
9060 int direction = MovDir[x][y];
9061 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9062 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9063 int newx = x + dx, newy = y + dy;
9064 int stored = Store[x][y];
9065 int stored_new = Store[newx][newy];
9066 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9067 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9068 boolean last_line = (newy == lev_fieldy - 1);
9070 MovPos[x][y] += getElementMoveStepsize(x, y);
9072 if (pushed_by_player) /* special case: moving object pushed by player */
9073 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9075 if (ABS(MovPos[x][y]) < TILEX)
9078 int ee = Feld[x][y];
9079 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9080 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9082 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9083 x, y, ABS(MovPos[x][y]),
9085 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9088 TEST_DrawLevelField(x, y);
9090 return; /* element is still moving */
9093 /* element reached destination field */
9095 Feld[x][y] = EL_EMPTY;
9096 Feld[newx][newy] = element;
9097 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9099 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9101 element = Feld[newx][newy] = EL_ACID;
9103 else if (element == EL_MOLE)
9105 Feld[x][y] = EL_SAND;
9107 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9109 else if (element == EL_QUICKSAND_FILLING)
9111 element = Feld[newx][newy] = get_next_element(element);
9112 Store[newx][newy] = Store[x][y];
9114 else if (element == EL_QUICKSAND_EMPTYING)
9116 Feld[x][y] = get_next_element(element);
9117 element = Feld[newx][newy] = Store[x][y];
9119 else if (element == EL_QUICKSAND_FAST_FILLING)
9121 element = Feld[newx][newy] = get_next_element(element);
9122 Store[newx][newy] = Store[x][y];
9124 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9126 Feld[x][y] = get_next_element(element);
9127 element = Feld[newx][newy] = Store[x][y];
9129 else if (element == EL_MAGIC_WALL_FILLING)
9131 element = Feld[newx][newy] = get_next_element(element);
9132 if (!game.magic_wall_active)
9133 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9134 Store[newx][newy] = Store[x][y];
9136 else if (element == EL_MAGIC_WALL_EMPTYING)
9138 Feld[x][y] = get_next_element(element);
9139 if (!game.magic_wall_active)
9140 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9141 element = Feld[newx][newy] = Store[x][y];
9143 #if USE_NEW_CUSTOM_VALUE
9144 InitField(newx, newy, FALSE);
9147 else if (element == EL_BD_MAGIC_WALL_FILLING)
9149 element = Feld[newx][newy] = get_next_element(element);
9150 if (!game.magic_wall_active)
9151 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9152 Store[newx][newy] = Store[x][y];
9154 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9156 Feld[x][y] = get_next_element(element);
9157 if (!game.magic_wall_active)
9158 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9159 element = Feld[newx][newy] = Store[x][y];
9161 #if USE_NEW_CUSTOM_VALUE
9162 InitField(newx, newy, FALSE);
9165 else if (element == EL_DC_MAGIC_WALL_FILLING)
9167 element = Feld[newx][newy] = get_next_element(element);
9168 if (!game.magic_wall_active)
9169 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9170 Store[newx][newy] = Store[x][y];
9172 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9174 Feld[x][y] = get_next_element(element);
9175 if (!game.magic_wall_active)
9176 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9177 element = Feld[newx][newy] = Store[x][y];
9179 #if USE_NEW_CUSTOM_VALUE
9180 InitField(newx, newy, FALSE);
9183 else if (element == EL_AMOEBA_DROPPING)
9185 Feld[x][y] = get_next_element(element);
9186 element = Feld[newx][newy] = Store[x][y];
9188 else if (element == EL_SOKOBAN_OBJECT)
9191 Feld[x][y] = Back[x][y];
9193 if (Back[newx][newy])
9194 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9196 Back[x][y] = Back[newx][newy] = 0;
9199 Store[x][y] = EL_EMPTY;
9204 MovDelay[newx][newy] = 0;
9206 if (CAN_CHANGE_OR_HAS_ACTION(element))
9208 /* copy element change control values to new field */
9209 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9210 ChangePage[newx][newy] = ChangePage[x][y];
9211 ChangeCount[newx][newy] = ChangeCount[x][y];
9212 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9215 #if USE_NEW_CUSTOM_VALUE
9216 CustomValue[newx][newy] = CustomValue[x][y];
9219 ChangeDelay[x][y] = 0;
9220 ChangePage[x][y] = -1;
9221 ChangeCount[x][y] = 0;
9222 ChangeEvent[x][y] = -1;
9224 #if USE_NEW_CUSTOM_VALUE
9225 CustomValue[x][y] = 0;
9228 /* copy animation control values to new field */
9229 GfxFrame[newx][newy] = GfxFrame[x][y];
9230 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9231 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9232 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9234 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9236 /* some elements can leave other elements behind after moving */
9238 if (ei->move_leave_element != EL_EMPTY &&
9239 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9240 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9242 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9243 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9244 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9247 int move_leave_element = ei->move_leave_element;
9251 /* this makes it possible to leave the removed element again */
9252 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9253 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9255 /* this makes it possible to leave the removed element again */
9256 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9257 move_leave_element = stored;
9260 /* this makes it possible to leave the removed element again */
9261 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9262 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9263 move_leave_element = stored;
9266 Feld[x][y] = move_leave_element;
9268 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9269 MovDir[x][y] = direction;
9271 InitField(x, y, FALSE);
9273 if (GFX_CRUMBLED(Feld[x][y]))
9274 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9276 if (ELEM_IS_PLAYER(move_leave_element))
9277 RelocatePlayer(x, y, move_leave_element);
9280 /* do this after checking for left-behind element */
9281 ResetGfxAnimation(x, y); /* reset animation values for old field */
9283 if (!CAN_MOVE(element) ||
9284 (CAN_FALL(element) && direction == MV_DOWN &&
9285 (element == EL_SPRING ||
9286 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9287 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9288 GfxDir[x][y] = MovDir[newx][newy] = 0;
9290 TEST_DrawLevelField(x, y);
9291 TEST_DrawLevelField(newx, newy);
9293 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9295 /* prevent pushed element from moving on in pushed direction */
9296 if (pushed_by_player && CAN_MOVE(element) &&
9297 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9298 !(element_info[element].move_pattern & direction))
9299 TurnRound(newx, newy);
9301 /* prevent elements on conveyor belt from moving on in last direction */
9302 if (pushed_by_conveyor && CAN_FALL(element) &&
9303 direction & MV_HORIZONTAL)
9304 MovDir[newx][newy] = 0;
9306 if (!pushed_by_player)
9308 int nextx = newx + dx, nexty = newy + dy;
9309 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9311 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9313 if (CAN_FALL(element) && direction == MV_DOWN)
9314 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9316 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9317 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9319 #if USE_FIX_IMPACT_COLLISION
9320 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9321 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9325 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9327 TestIfBadThingTouchesPlayer(newx, newy);
9328 TestIfBadThingTouchesFriend(newx, newy);
9330 if (!IS_CUSTOM_ELEMENT(element))
9331 TestIfBadThingTouchesOtherBadThing(newx, newy);
9333 else if (element == EL_PENGUIN)
9334 TestIfFriendTouchesBadThing(newx, newy);
9336 if (DONT_GET_HIT_BY(element))
9338 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9341 /* give the player one last chance (one more frame) to move away */
9342 if (CAN_FALL(element) && direction == MV_DOWN &&
9343 (last_line || (!IS_FREE(x, newy + 1) &&
9344 (!IS_PLAYER(x, newy + 1) ||
9345 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9348 if (pushed_by_player && !game.use_change_when_pushing_bug)
9350 int push_side = MV_DIR_OPPOSITE(direction);
9351 struct PlayerInfo *player = PLAYERINFO(x, y);
9353 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9354 player->index_bit, push_side);
9355 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9356 player->index_bit, push_side);
9359 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9360 MovDelay[newx][newy] = 1;
9362 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9364 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9367 if (ChangePage[newx][newy] != -1) /* delayed change */
9369 int page = ChangePage[newx][newy];
9370 struct ElementChangeInfo *change = &ei->change_page[page];
9372 ChangePage[newx][newy] = -1;
9374 if (change->can_change)
9376 if (ChangeElement(newx, newy, element, page))
9378 if (change->post_change_function)
9379 change->post_change_function(newx, newy);
9383 if (change->has_action)
9384 ExecuteCustomElementAction(newx, newy, element, page);
9388 TestIfElementHitsCustomElement(newx, newy, direction);
9389 TestIfPlayerTouchesCustomElement(newx, newy);
9390 TestIfElementTouchesCustomElement(newx, newy);
9392 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9393 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9394 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9395 MV_DIR_OPPOSITE(direction));
9398 int AmoebeNachbarNr(int ax, int ay)
9401 int element = Feld[ax][ay];
9403 static int xy[4][2] =
9411 for (i = 0; i < NUM_DIRECTIONS; i++)
9413 int x = ax + xy[i][0];
9414 int y = ay + xy[i][1];
9416 if (!IN_LEV_FIELD(x, y))
9419 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9420 group_nr = AmoebaNr[x][y];
9426 void AmoebenVereinigen(int ax, int ay)
9428 int i, x, y, xx, yy;
9429 int new_group_nr = AmoebaNr[ax][ay];
9430 static int xy[4][2] =
9438 if (new_group_nr == 0)
9441 for (i = 0; i < NUM_DIRECTIONS; i++)
9446 if (!IN_LEV_FIELD(x, y))
9449 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9450 Feld[x][y] == EL_BD_AMOEBA ||
9451 Feld[x][y] == EL_AMOEBA_DEAD) &&
9452 AmoebaNr[x][y] != new_group_nr)
9454 int old_group_nr = AmoebaNr[x][y];
9456 if (old_group_nr == 0)
9459 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9460 AmoebaCnt[old_group_nr] = 0;
9461 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9462 AmoebaCnt2[old_group_nr] = 0;
9464 SCAN_PLAYFIELD(xx, yy)
9466 if (AmoebaNr[xx][yy] == old_group_nr)
9467 AmoebaNr[xx][yy] = new_group_nr;
9473 void AmoebeUmwandeln(int ax, int ay)
9477 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9479 int group_nr = AmoebaNr[ax][ay];
9484 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9485 printf("AmoebeUmwandeln(): This should never happen!\n");
9490 SCAN_PLAYFIELD(x, y)
9492 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9495 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9499 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9500 SND_AMOEBA_TURNING_TO_GEM :
9501 SND_AMOEBA_TURNING_TO_ROCK));
9506 static int xy[4][2] =
9514 for (i = 0; i < NUM_DIRECTIONS; i++)
9519 if (!IN_LEV_FIELD(x, y))
9522 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9524 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9525 SND_AMOEBA_TURNING_TO_GEM :
9526 SND_AMOEBA_TURNING_TO_ROCK));
9533 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9536 int group_nr = AmoebaNr[ax][ay];
9537 boolean done = FALSE;
9542 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9543 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9548 SCAN_PLAYFIELD(x, y)
9550 if (AmoebaNr[x][y] == group_nr &&
9551 (Feld[x][y] == EL_AMOEBA_DEAD ||
9552 Feld[x][y] == EL_BD_AMOEBA ||
9553 Feld[x][y] == EL_AMOEBA_GROWING))
9556 Feld[x][y] = new_element;
9557 InitField(x, y, FALSE);
9558 TEST_DrawLevelField(x, y);
9564 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9565 SND_BD_AMOEBA_TURNING_TO_ROCK :
9566 SND_BD_AMOEBA_TURNING_TO_GEM));
9569 void AmoebeWaechst(int x, int y)
9571 static unsigned int sound_delay = 0;
9572 static unsigned int sound_delay_value = 0;
9574 if (!MovDelay[x][y]) /* start new growing cycle */
9578 if (DelayReached(&sound_delay, sound_delay_value))
9580 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9581 sound_delay_value = 30;
9585 if (MovDelay[x][y]) /* wait some time before growing bigger */
9588 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9590 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9591 6 - MovDelay[x][y]);
9593 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9596 if (!MovDelay[x][y])
9598 Feld[x][y] = Store[x][y];
9600 TEST_DrawLevelField(x, y);
9605 void AmoebaDisappearing(int x, int y)
9607 static unsigned int sound_delay = 0;
9608 static unsigned int sound_delay_value = 0;
9610 if (!MovDelay[x][y]) /* start new shrinking cycle */
9614 if (DelayReached(&sound_delay, sound_delay_value))
9615 sound_delay_value = 30;
9618 if (MovDelay[x][y]) /* wait some time before shrinking */
9621 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9623 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9624 6 - MovDelay[x][y]);
9626 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9629 if (!MovDelay[x][y])
9631 Feld[x][y] = EL_EMPTY;
9632 TEST_DrawLevelField(x, y);
9634 /* don't let mole enter this field in this cycle;
9635 (give priority to objects falling to this field from above) */
9641 void AmoebeAbleger(int ax, int ay)
9644 int element = Feld[ax][ay];
9645 int graphic = el2img(element);
9646 int newax = ax, neway = ay;
9647 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9648 static int xy[4][2] =
9656 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9658 Feld[ax][ay] = EL_AMOEBA_DEAD;
9659 TEST_DrawLevelField(ax, ay);
9663 if (IS_ANIMATED(graphic))
9664 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9666 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9667 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9669 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9672 if (MovDelay[ax][ay])
9676 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9679 int x = ax + xy[start][0];
9680 int y = ay + xy[start][1];
9682 if (!IN_LEV_FIELD(x, y))
9685 if (IS_FREE(x, y) ||
9686 CAN_GROW_INTO(Feld[x][y]) ||
9687 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9688 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9694 if (newax == ax && neway == ay)
9697 else /* normal or "filled" (BD style) amoeba */
9700 boolean waiting_for_player = FALSE;
9702 for (i = 0; i < NUM_DIRECTIONS; i++)
9704 int j = (start + i) % 4;
9705 int x = ax + xy[j][0];
9706 int y = ay + xy[j][1];
9708 if (!IN_LEV_FIELD(x, y))
9711 if (IS_FREE(x, y) ||
9712 CAN_GROW_INTO(Feld[x][y]) ||
9713 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9714 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9720 else if (IS_PLAYER(x, y))
9721 waiting_for_player = TRUE;
9724 if (newax == ax && neway == ay) /* amoeba cannot grow */
9726 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9728 Feld[ax][ay] = EL_AMOEBA_DEAD;
9729 TEST_DrawLevelField(ax, ay);
9730 AmoebaCnt[AmoebaNr[ax][ay]]--;
9732 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9734 if (element == EL_AMOEBA_FULL)
9735 AmoebeUmwandeln(ax, ay);
9736 else if (element == EL_BD_AMOEBA)
9737 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9742 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9744 /* amoeba gets larger by growing in some direction */
9746 int new_group_nr = AmoebaNr[ax][ay];
9749 if (new_group_nr == 0)
9751 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9752 printf("AmoebeAbleger(): This should never happen!\n");
9757 AmoebaNr[newax][neway] = new_group_nr;
9758 AmoebaCnt[new_group_nr]++;
9759 AmoebaCnt2[new_group_nr]++;
9761 /* if amoeba touches other amoeba(s) after growing, unify them */
9762 AmoebenVereinigen(newax, neway);
9764 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9766 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9772 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9773 (neway == lev_fieldy - 1 && newax != ax))
9775 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9776 Store[newax][neway] = element;
9778 else if (neway == ay || element == EL_EMC_DRIPPER)
9780 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9782 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9786 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9787 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9788 Store[ax][ay] = EL_AMOEBA_DROP;
9789 ContinueMoving(ax, ay);
9793 TEST_DrawLevelField(newax, neway);
9796 void Life(int ax, int ay)
9800 int element = Feld[ax][ay];
9801 int graphic = el2img(element);
9802 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9804 boolean changed = FALSE;
9806 if (IS_ANIMATED(graphic))
9807 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9812 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9813 MovDelay[ax][ay] = life_time;
9815 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9818 if (MovDelay[ax][ay])
9822 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9824 int xx = ax+x1, yy = ay+y1;
9827 if (!IN_LEV_FIELD(xx, yy))
9830 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9832 int x = xx+x2, y = yy+y2;
9834 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9837 if (((Feld[x][y] == element ||
9838 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9840 (IS_FREE(x, y) && Stop[x][y]))
9844 if (xx == ax && yy == ay) /* field in the middle */
9846 if (nachbarn < life_parameter[0] ||
9847 nachbarn > life_parameter[1])
9849 Feld[xx][yy] = EL_EMPTY;
9851 TEST_DrawLevelField(xx, yy);
9852 Stop[xx][yy] = TRUE;
9856 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9857 { /* free border field */
9858 if (nachbarn >= life_parameter[2] &&
9859 nachbarn <= life_parameter[3])
9861 Feld[xx][yy] = element;
9862 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9864 TEST_DrawLevelField(xx, yy);
9865 Stop[xx][yy] = TRUE;
9872 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9873 SND_GAME_OF_LIFE_GROWING);
9876 static void InitRobotWheel(int x, int y)
9878 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9881 static void RunRobotWheel(int x, int y)
9883 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9886 static void StopRobotWheel(int x, int y)
9888 if (ZX == x && ZY == y)
9892 game.robot_wheel_active = FALSE;
9896 static void InitTimegateWheel(int x, int y)
9898 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9901 static void RunTimegateWheel(int x, int y)
9903 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9906 static void InitMagicBallDelay(int x, int y)
9909 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9911 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9915 static void ActivateMagicBall(int bx, int by)
9919 if (level.ball_random)
9921 int pos_border = RND(8); /* select one of the eight border elements */
9922 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9923 int xx = pos_content % 3;
9924 int yy = pos_content / 3;
9929 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9930 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9934 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9936 int xx = x - bx + 1;
9937 int yy = y - by + 1;
9939 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9940 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9944 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9947 void CheckExit(int x, int y)
9949 if (local_player->gems_still_needed > 0 ||
9950 local_player->sokobanfields_still_needed > 0 ||
9951 local_player->lights_still_needed > 0)
9953 int element = Feld[x][y];
9954 int graphic = el2img(element);
9956 if (IS_ANIMATED(graphic))
9957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9962 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9965 Feld[x][y] = EL_EXIT_OPENING;
9967 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9970 void CheckExitEM(int x, int y)
9972 if (local_player->gems_still_needed > 0 ||
9973 local_player->sokobanfields_still_needed > 0 ||
9974 local_player->lights_still_needed > 0)
9976 int element = Feld[x][y];
9977 int graphic = el2img(element);
9979 if (IS_ANIMATED(graphic))
9980 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9985 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9988 Feld[x][y] = EL_EM_EXIT_OPENING;
9990 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9993 void CheckExitSteel(int x, int y)
9995 if (local_player->gems_still_needed > 0 ||
9996 local_player->sokobanfields_still_needed > 0 ||
9997 local_player->lights_still_needed > 0)
9999 int element = Feld[x][y];
10000 int graphic = el2img(element);
10002 if (IS_ANIMATED(graphic))
10003 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10008 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10011 Feld[x][y] = EL_STEEL_EXIT_OPENING;
10013 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
10016 void CheckExitSteelEM(int x, int y)
10018 if (local_player->gems_still_needed > 0 ||
10019 local_player->sokobanfields_still_needed > 0 ||
10020 local_player->lights_still_needed > 0)
10022 int element = Feld[x][y];
10023 int graphic = el2img(element);
10025 if (IS_ANIMATED(graphic))
10026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10031 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10034 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
10036 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
10039 void CheckExitSP(int x, int y)
10041 if (local_player->gems_still_needed > 0)
10043 int element = Feld[x][y];
10044 int graphic = el2img(element);
10046 if (IS_ANIMATED(graphic))
10047 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10052 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10055 Feld[x][y] = EL_SP_EXIT_OPENING;
10057 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10060 static void CloseAllOpenTimegates()
10064 SCAN_PLAYFIELD(x, y)
10066 int element = Feld[x][y];
10068 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10070 Feld[x][y] = EL_TIMEGATE_CLOSING;
10072 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10077 void DrawTwinkleOnField(int x, int y)
10079 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10082 if (Feld[x][y] == EL_BD_DIAMOND)
10085 if (MovDelay[x][y] == 0) /* next animation frame */
10086 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10088 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10092 DrawLevelElementAnimation(x, y, Feld[x][y]);
10094 if (MovDelay[x][y] != 0)
10096 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10097 10 - MovDelay[x][y]);
10099 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10104 void MauerWaechst(int x, int y)
10108 if (!MovDelay[x][y]) /* next animation frame */
10109 MovDelay[x][y] = 3 * delay;
10111 if (MovDelay[x][y]) /* wait some time before next frame */
10115 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10117 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10118 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10120 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10123 if (!MovDelay[x][y])
10125 if (MovDir[x][y] == MV_LEFT)
10127 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10128 TEST_DrawLevelField(x - 1, y);
10130 else if (MovDir[x][y] == MV_RIGHT)
10132 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10133 TEST_DrawLevelField(x + 1, y);
10135 else if (MovDir[x][y] == MV_UP)
10137 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10138 TEST_DrawLevelField(x, y - 1);
10142 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10143 TEST_DrawLevelField(x, y + 1);
10146 Feld[x][y] = Store[x][y];
10148 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10149 TEST_DrawLevelField(x, y);
10154 void MauerAbleger(int ax, int ay)
10156 int element = Feld[ax][ay];
10157 int graphic = el2img(element);
10158 boolean oben_frei = FALSE, unten_frei = FALSE;
10159 boolean links_frei = FALSE, rechts_frei = FALSE;
10160 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10161 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10162 boolean new_wall = FALSE;
10164 if (IS_ANIMATED(graphic))
10165 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10167 if (!MovDelay[ax][ay]) /* start building new wall */
10168 MovDelay[ax][ay] = 6;
10170 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10172 MovDelay[ax][ay]--;
10173 if (MovDelay[ax][ay])
10177 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10179 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10181 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10183 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10184 rechts_frei = TRUE;
10186 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10187 element == EL_EXPANDABLE_WALL_ANY)
10191 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10192 Store[ax][ay-1] = element;
10193 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10194 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10195 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10196 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10201 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10202 Store[ax][ay+1] = element;
10203 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10204 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10205 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10206 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10211 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10212 element == EL_EXPANDABLE_WALL_ANY ||
10213 element == EL_EXPANDABLE_WALL ||
10214 element == EL_BD_EXPANDABLE_WALL)
10218 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10219 Store[ax-1][ay] = element;
10220 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10221 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10222 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10223 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10229 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10230 Store[ax+1][ay] = element;
10231 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10232 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10233 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10234 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10239 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10240 TEST_DrawLevelField(ax, ay);
10242 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10243 oben_massiv = TRUE;
10244 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10245 unten_massiv = TRUE;
10246 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10247 links_massiv = TRUE;
10248 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10249 rechts_massiv = TRUE;
10251 if (((oben_massiv && unten_massiv) ||
10252 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10253 element == EL_EXPANDABLE_WALL) &&
10254 ((links_massiv && rechts_massiv) ||
10255 element == EL_EXPANDABLE_WALL_VERTICAL))
10256 Feld[ax][ay] = EL_WALL;
10259 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10262 void MauerAblegerStahl(int ax, int ay)
10264 int element = Feld[ax][ay];
10265 int graphic = el2img(element);
10266 boolean oben_frei = FALSE, unten_frei = FALSE;
10267 boolean links_frei = FALSE, rechts_frei = FALSE;
10268 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10269 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10270 boolean new_wall = FALSE;
10272 if (IS_ANIMATED(graphic))
10273 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10275 if (!MovDelay[ax][ay]) /* start building new wall */
10276 MovDelay[ax][ay] = 6;
10278 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10280 MovDelay[ax][ay]--;
10281 if (MovDelay[ax][ay])
10285 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10287 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10289 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10291 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10292 rechts_frei = TRUE;
10294 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10295 element == EL_EXPANDABLE_STEELWALL_ANY)
10299 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10300 Store[ax][ay-1] = element;
10301 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10302 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10303 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10304 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10309 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10310 Store[ax][ay+1] = element;
10311 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10312 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10313 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10314 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10319 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10320 element == EL_EXPANDABLE_STEELWALL_ANY)
10324 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10325 Store[ax-1][ay] = element;
10326 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10327 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10328 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10329 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10335 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10336 Store[ax+1][ay] = element;
10337 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10338 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10339 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10340 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10345 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10346 oben_massiv = TRUE;
10347 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10348 unten_massiv = TRUE;
10349 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10350 links_massiv = TRUE;
10351 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10352 rechts_massiv = TRUE;
10354 if (((oben_massiv && unten_massiv) ||
10355 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10356 ((links_massiv && rechts_massiv) ||
10357 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10358 Feld[ax][ay] = EL_STEELWALL;
10361 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10364 void CheckForDragon(int x, int y)
10367 boolean dragon_found = FALSE;
10368 static int xy[4][2] =
10376 for (i = 0; i < NUM_DIRECTIONS; i++)
10378 for (j = 0; j < 4; j++)
10380 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10382 if (IN_LEV_FIELD(xx, yy) &&
10383 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10385 if (Feld[xx][yy] == EL_DRAGON)
10386 dragon_found = TRUE;
10395 for (i = 0; i < NUM_DIRECTIONS; i++)
10397 for (j = 0; j < 3; j++)
10399 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10401 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10403 Feld[xx][yy] = EL_EMPTY;
10404 TEST_DrawLevelField(xx, yy);
10413 static void InitBuggyBase(int x, int y)
10415 int element = Feld[x][y];
10416 int activating_delay = FRAMES_PER_SECOND / 4;
10418 ChangeDelay[x][y] =
10419 (element == EL_SP_BUGGY_BASE ?
10420 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10421 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10423 element == EL_SP_BUGGY_BASE_ACTIVE ?
10424 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10427 static void WarnBuggyBase(int x, int y)
10430 static int xy[4][2] =
10438 for (i = 0; i < NUM_DIRECTIONS; i++)
10440 int xx = x + xy[i][0];
10441 int yy = y + xy[i][1];
10443 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10445 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10452 static void InitTrap(int x, int y)
10454 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10457 static void ActivateTrap(int x, int y)
10459 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10462 static void ChangeActiveTrap(int x, int y)
10464 int graphic = IMG_TRAP_ACTIVE;
10466 /* if new animation frame was drawn, correct crumbled sand border */
10467 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10468 TEST_DrawLevelFieldCrumbled(x, y);
10471 static int getSpecialActionElement(int element, int number, int base_element)
10473 return (element != EL_EMPTY ? element :
10474 number != -1 ? base_element + number - 1 :
10478 static int getModifiedActionNumber(int value_old, int operator, int operand,
10479 int value_min, int value_max)
10481 int value_new = (operator == CA_MODE_SET ? operand :
10482 operator == CA_MODE_ADD ? value_old + operand :
10483 operator == CA_MODE_SUBTRACT ? value_old - operand :
10484 operator == CA_MODE_MULTIPLY ? value_old * operand :
10485 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10486 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10489 return (value_new < value_min ? value_min :
10490 value_new > value_max ? value_max :
10494 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10496 struct ElementInfo *ei = &element_info[element];
10497 struct ElementChangeInfo *change = &ei->change_page[page];
10498 int target_element = change->target_element;
10499 int action_type = change->action_type;
10500 int action_mode = change->action_mode;
10501 int action_arg = change->action_arg;
10502 int action_element = change->action_element;
10505 if (!change->has_action)
10508 /* ---------- determine action paramater values -------------------------- */
10510 int level_time_value =
10511 (level.time > 0 ? TimeLeft :
10514 int action_arg_element_raw =
10515 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10516 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10517 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10518 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10519 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10520 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10521 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10523 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10526 if (action_arg_element_raw == EL_GROUP_START)
10527 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10530 int action_arg_direction =
10531 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10532 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10533 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10534 change->actual_trigger_side :
10535 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10536 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10539 int action_arg_number_min =
10540 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10543 int action_arg_number_max =
10544 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10545 action_type == CA_SET_LEVEL_GEMS ? 999 :
10546 action_type == CA_SET_LEVEL_TIME ? 9999 :
10547 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10548 action_type == CA_SET_CE_VALUE ? 9999 :
10549 action_type == CA_SET_CE_SCORE ? 9999 :
10552 int action_arg_number_reset =
10553 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10554 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10555 action_type == CA_SET_LEVEL_TIME ? level.time :
10556 action_type == CA_SET_LEVEL_SCORE ? 0 :
10557 #if USE_NEW_CUSTOM_VALUE
10558 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10560 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10562 action_type == CA_SET_CE_SCORE ? 0 :
10565 int action_arg_number =
10566 (action_arg <= CA_ARG_MAX ? action_arg :
10567 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10568 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10569 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10570 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10571 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10572 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10573 #if USE_NEW_CUSTOM_VALUE
10574 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10576 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10578 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10579 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10580 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10581 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10582 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10583 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10584 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10585 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10586 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10587 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10588 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10589 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10590 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10591 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10594 int action_arg_number_old =
10595 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10596 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10597 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10598 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10599 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10602 int action_arg_number_new =
10603 getModifiedActionNumber(action_arg_number_old,
10604 action_mode, action_arg_number,
10605 action_arg_number_min, action_arg_number_max);
10608 int trigger_player_bits =
10609 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10610 change->actual_trigger_player_bits : change->trigger_player);
10612 int trigger_player_bits =
10613 (change->actual_trigger_player >= EL_PLAYER_1 &&
10614 change->actual_trigger_player <= EL_PLAYER_4 ?
10615 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10619 int action_arg_player_bits =
10620 (action_arg >= CA_ARG_PLAYER_1 &&
10621 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10622 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10623 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10626 /* ---------- execute action -------------------------------------------- */
10628 switch (action_type)
10635 /* ---------- level actions ------------------------------------------- */
10637 case CA_RESTART_LEVEL:
10639 game.restart_level = TRUE;
10644 case CA_SHOW_ENVELOPE:
10646 int element = getSpecialActionElement(action_arg_element,
10647 action_arg_number, EL_ENVELOPE_1);
10649 if (IS_ENVELOPE(element))
10650 local_player->show_envelope = element;
10655 case CA_SET_LEVEL_TIME:
10657 if (level.time > 0) /* only modify limited time value */
10659 TimeLeft = action_arg_number_new;
10662 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10664 DisplayGameControlValues();
10666 DrawGameValue_Time(TimeLeft);
10669 if (!TimeLeft && setup.time_limit)
10670 for (i = 0; i < MAX_PLAYERS; i++)
10671 KillPlayer(&stored_player[i]);
10677 case CA_SET_LEVEL_SCORE:
10679 local_player->score = action_arg_number_new;
10682 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10684 DisplayGameControlValues();
10686 DrawGameValue_Score(local_player->score);
10692 case CA_SET_LEVEL_GEMS:
10694 local_player->gems_still_needed = action_arg_number_new;
10697 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10699 DisplayGameControlValues();
10701 DrawGameValue_Emeralds(local_player->gems_still_needed);
10707 #if !USE_PLAYER_GRAVITY
10708 case CA_SET_LEVEL_GRAVITY:
10710 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10711 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10712 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10718 case CA_SET_LEVEL_WIND:
10720 game.wind_direction = action_arg_direction;
10725 case CA_SET_LEVEL_RANDOM_SEED:
10728 /* ensure that setting a new random seed while playing is predictable */
10729 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10731 InitRND(action_arg_number_new);
10735 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10743 for (i = 0; i < 9; i++)
10744 printf("%d, ", RND(2));
10752 /* ---------- player actions ------------------------------------------ */
10754 case CA_MOVE_PLAYER:
10756 /* automatically move to the next field in specified direction */
10757 for (i = 0; i < MAX_PLAYERS; i++)
10758 if (trigger_player_bits & (1 << i))
10759 stored_player[i].programmed_action = action_arg_direction;
10764 case CA_EXIT_PLAYER:
10766 for (i = 0; i < MAX_PLAYERS; i++)
10767 if (action_arg_player_bits & (1 << i))
10768 PlayerWins(&stored_player[i]);
10773 case CA_KILL_PLAYER:
10775 for (i = 0; i < MAX_PLAYERS; i++)
10776 if (action_arg_player_bits & (1 << i))
10777 KillPlayer(&stored_player[i]);
10782 case CA_SET_PLAYER_KEYS:
10784 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10785 int element = getSpecialActionElement(action_arg_element,
10786 action_arg_number, EL_KEY_1);
10788 if (IS_KEY(element))
10790 for (i = 0; i < MAX_PLAYERS; i++)
10792 if (trigger_player_bits & (1 << i))
10794 stored_player[i].key[KEY_NR(element)] = key_state;
10796 DrawGameDoorValues();
10804 case CA_SET_PLAYER_SPEED:
10807 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10810 for (i = 0; i < MAX_PLAYERS; i++)
10812 if (trigger_player_bits & (1 << i))
10814 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10816 if (action_arg == CA_ARG_SPEED_FASTER &&
10817 stored_player[i].cannot_move)
10819 action_arg_number = STEPSIZE_VERY_SLOW;
10821 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10822 action_arg == CA_ARG_SPEED_FASTER)
10824 action_arg_number = 2;
10825 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10828 else if (action_arg == CA_ARG_NUMBER_RESET)
10830 action_arg_number = level.initial_player_stepsize[i];
10834 getModifiedActionNumber(move_stepsize,
10837 action_arg_number_min,
10838 action_arg_number_max);
10840 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10847 case CA_SET_PLAYER_SHIELD:
10849 for (i = 0; i < MAX_PLAYERS; i++)
10851 if (trigger_player_bits & (1 << i))
10853 if (action_arg == CA_ARG_SHIELD_OFF)
10855 stored_player[i].shield_normal_time_left = 0;
10856 stored_player[i].shield_deadly_time_left = 0;
10858 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10860 stored_player[i].shield_normal_time_left = 999999;
10862 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10864 stored_player[i].shield_normal_time_left = 999999;
10865 stored_player[i].shield_deadly_time_left = 999999;
10873 #if USE_PLAYER_GRAVITY
10874 case CA_SET_PLAYER_GRAVITY:
10876 for (i = 0; i < MAX_PLAYERS; i++)
10878 if (trigger_player_bits & (1 << i))
10880 stored_player[i].gravity =
10881 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10882 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10883 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10884 stored_player[i].gravity);
10892 case CA_SET_PLAYER_ARTWORK:
10894 for (i = 0; i < MAX_PLAYERS; i++)
10896 if (trigger_player_bits & (1 << i))
10898 int artwork_element = action_arg_element;
10900 if (action_arg == CA_ARG_ELEMENT_RESET)
10902 (level.use_artwork_element[i] ? level.artwork_element[i] :
10903 stored_player[i].element_nr);
10905 #if USE_GFX_RESET_PLAYER_ARTWORK
10906 if (stored_player[i].artwork_element != artwork_element)
10907 stored_player[i].Frame = 0;
10910 stored_player[i].artwork_element = artwork_element;
10912 SetPlayerWaiting(&stored_player[i], FALSE);
10914 /* set number of special actions for bored and sleeping animation */
10915 stored_player[i].num_special_action_bored =
10916 get_num_special_action(artwork_element,
10917 ACTION_BORING_1, ACTION_BORING_LAST);
10918 stored_player[i].num_special_action_sleeping =
10919 get_num_special_action(artwork_element,
10920 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10927 case CA_SET_PLAYER_INVENTORY:
10929 for (i = 0; i < MAX_PLAYERS; i++)
10931 struct PlayerInfo *player = &stored_player[i];
10934 if (trigger_player_bits & (1 << i))
10936 int inventory_element = action_arg_element;
10938 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10939 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10940 action_arg == CA_ARG_ELEMENT_ACTION)
10942 int element = inventory_element;
10943 int collect_count = element_info[element].collect_count_initial;
10945 if (!IS_CUSTOM_ELEMENT(element))
10948 if (collect_count == 0)
10949 player->inventory_infinite_element = element;
10951 for (k = 0; k < collect_count; k++)
10952 if (player->inventory_size < MAX_INVENTORY_SIZE)
10953 player->inventory_element[player->inventory_size++] =
10956 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10957 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10958 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10960 if (player->inventory_infinite_element != EL_UNDEFINED &&
10961 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10962 action_arg_element_raw))
10963 player->inventory_infinite_element = EL_UNDEFINED;
10965 for (k = 0, j = 0; j < player->inventory_size; j++)
10967 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10968 action_arg_element_raw))
10969 player->inventory_element[k++] = player->inventory_element[j];
10972 player->inventory_size = k;
10974 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10976 if (player->inventory_size > 0)
10978 for (j = 0; j < player->inventory_size - 1; j++)
10979 player->inventory_element[j] = player->inventory_element[j + 1];
10981 player->inventory_size--;
10984 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10986 if (player->inventory_size > 0)
10987 player->inventory_size--;
10989 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10991 player->inventory_infinite_element = EL_UNDEFINED;
10992 player->inventory_size = 0;
10994 else if (action_arg == CA_ARG_INVENTORY_RESET)
10996 player->inventory_infinite_element = EL_UNDEFINED;
10997 player->inventory_size = 0;
10999 if (level.use_initial_inventory[i])
11001 for (j = 0; j < level.initial_inventory_size[i]; j++)
11003 int element = level.initial_inventory_content[i][j];
11004 int collect_count = element_info[element].collect_count_initial;
11006 if (!IS_CUSTOM_ELEMENT(element))
11009 if (collect_count == 0)
11010 player->inventory_infinite_element = element;
11012 for (k = 0; k < collect_count; k++)
11013 if (player->inventory_size < MAX_INVENTORY_SIZE)
11014 player->inventory_element[player->inventory_size++] =
11025 /* ---------- CE actions ---------------------------------------------- */
11027 case CA_SET_CE_VALUE:
11029 #if USE_NEW_CUSTOM_VALUE
11030 int last_ce_value = CustomValue[x][y];
11032 CustomValue[x][y] = action_arg_number_new;
11034 if (CustomValue[x][y] != last_ce_value)
11036 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
11037 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
11039 if (CustomValue[x][y] == 0)
11041 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
11042 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11050 case CA_SET_CE_SCORE:
11052 #if USE_NEW_CUSTOM_VALUE
11053 int last_ce_score = ei->collect_score;
11055 ei->collect_score = action_arg_number_new;
11057 if (ei->collect_score != last_ce_score)
11059 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11060 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11062 if (ei->collect_score == 0)
11066 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11067 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11070 This is a very special case that seems to be a mixture between
11071 CheckElementChange() and CheckTriggeredElementChange(): while
11072 the first one only affects single elements that are triggered
11073 directly, the second one affects multiple elements in the playfield
11074 that are triggered indirectly by another element. This is a third
11075 case: Changing the CE score always affects multiple identical CEs,
11076 so every affected CE must be checked, not only the single CE for
11077 which the CE score was changed in the first place (as every instance
11078 of that CE shares the same CE score, and therefore also can change)!
11080 SCAN_PLAYFIELD(xx, yy)
11082 if (Feld[xx][yy] == element)
11083 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11084 CE_SCORE_GETS_ZERO);
11093 case CA_SET_CE_ARTWORK:
11095 int artwork_element = action_arg_element;
11096 boolean reset_frame = FALSE;
11099 if (action_arg == CA_ARG_ELEMENT_RESET)
11100 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11103 if (ei->gfx_element != artwork_element)
11104 reset_frame = TRUE;
11106 ei->gfx_element = artwork_element;
11108 SCAN_PLAYFIELD(xx, yy)
11110 if (Feld[xx][yy] == element)
11114 ResetGfxAnimation(xx, yy);
11115 ResetRandomAnimationValue(xx, yy);
11118 TEST_DrawLevelField(xx, yy);
11125 /* ---------- engine actions ------------------------------------------ */
11127 case CA_SET_ENGINE_SCAN_MODE:
11129 InitPlayfieldScanMode(action_arg);
11139 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11141 int old_element = Feld[x][y];
11142 int new_element = GetElementFromGroupElement(element);
11143 int previous_move_direction = MovDir[x][y];
11144 #if USE_NEW_CUSTOM_VALUE
11145 int last_ce_value = CustomValue[x][y];
11147 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11148 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11149 boolean add_player_onto_element = (new_element_is_player &&
11150 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11151 /* this breaks SnakeBite when a snake is
11152 halfway through a door that closes */
11153 /* NOW FIXED AT LEVEL INIT IN files.c */
11154 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11156 IS_WALKABLE(old_element));
11159 /* check if element under the player changes from accessible to unaccessible
11160 (needed for special case of dropping element which then changes) */
11161 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11162 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11170 if (!add_player_onto_element)
11172 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11173 RemoveMovingField(x, y);
11177 Feld[x][y] = new_element;
11179 #if !USE_GFX_RESET_GFX_ANIMATION
11180 ResetGfxAnimation(x, y);
11181 ResetRandomAnimationValue(x, y);
11184 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11185 MovDir[x][y] = previous_move_direction;
11187 #if USE_NEW_CUSTOM_VALUE
11188 if (element_info[new_element].use_last_ce_value)
11189 CustomValue[x][y] = last_ce_value;
11192 InitField_WithBug1(x, y, FALSE);
11194 new_element = Feld[x][y]; /* element may have changed */
11196 #if USE_GFX_RESET_GFX_ANIMATION
11197 ResetGfxAnimation(x, y);
11198 ResetRandomAnimationValue(x, y);
11201 TEST_DrawLevelField(x, y);
11203 if (GFX_CRUMBLED(new_element))
11204 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11208 /* check if element under the player changes from accessible to unaccessible
11209 (needed for special case of dropping element which then changes) */
11210 /* (must be checked after creating new element for walkable group elements) */
11211 #if USE_FIX_KILLED_BY_NON_WALKABLE
11212 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11213 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11220 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11221 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11230 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11231 if (new_element_is_player)
11232 RelocatePlayer(x, y, new_element);
11235 ChangeCount[x][y]++; /* count number of changes in the same frame */
11237 TestIfBadThingTouchesPlayer(x, y);
11238 TestIfPlayerTouchesCustomElement(x, y);
11239 TestIfElementTouchesCustomElement(x, y);
11242 static void CreateField(int x, int y, int element)
11244 CreateFieldExt(x, y, element, FALSE);
11247 static void CreateElementFromChange(int x, int y, int element)
11249 element = GET_VALID_RUNTIME_ELEMENT(element);
11251 #if USE_STOP_CHANGED_ELEMENTS
11252 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11254 int old_element = Feld[x][y];
11256 /* prevent changed element from moving in same engine frame
11257 unless both old and new element can either fall or move */
11258 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11259 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11264 CreateFieldExt(x, y, element, TRUE);
11267 static boolean ChangeElement(int x, int y, int element, int page)
11269 struct ElementInfo *ei = &element_info[element];
11270 struct ElementChangeInfo *change = &ei->change_page[page];
11271 int ce_value = CustomValue[x][y];
11272 int ce_score = ei->collect_score;
11273 int target_element;
11274 int old_element = Feld[x][y];
11276 /* always use default change event to prevent running into a loop */
11277 if (ChangeEvent[x][y] == -1)
11278 ChangeEvent[x][y] = CE_DELAY;
11280 if (ChangeEvent[x][y] == CE_DELAY)
11282 /* reset actual trigger element, trigger player and action element */
11283 change->actual_trigger_element = EL_EMPTY;
11284 change->actual_trigger_player = EL_EMPTY;
11285 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11286 change->actual_trigger_side = CH_SIDE_NONE;
11287 change->actual_trigger_ce_value = 0;
11288 change->actual_trigger_ce_score = 0;
11291 /* do not change elements more than a specified maximum number of changes */
11292 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11295 ChangeCount[x][y]++; /* count number of changes in the same frame */
11297 if (change->explode)
11304 if (change->use_target_content)
11306 boolean complete_replace = TRUE;
11307 boolean can_replace[3][3];
11310 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11313 boolean is_walkable;
11314 boolean is_diggable;
11315 boolean is_collectible;
11316 boolean is_removable;
11317 boolean is_destructible;
11318 int ex = x + xx - 1;
11319 int ey = y + yy - 1;
11320 int content_element = change->target_content.e[xx][yy];
11323 can_replace[xx][yy] = TRUE;
11325 if (ex == x && ey == y) /* do not check changing element itself */
11328 if (content_element == EL_EMPTY_SPACE)
11330 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11335 if (!IN_LEV_FIELD(ex, ey))
11337 can_replace[xx][yy] = FALSE;
11338 complete_replace = FALSE;
11345 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11346 e = MovingOrBlocked2Element(ex, ey);
11348 is_empty = (IS_FREE(ex, ey) ||
11349 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11351 is_walkable = (is_empty || IS_WALKABLE(e));
11352 is_diggable = (is_empty || IS_DIGGABLE(e));
11353 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11354 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11355 is_removable = (is_diggable || is_collectible);
11357 can_replace[xx][yy] =
11358 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11359 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11360 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11361 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11362 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11363 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11364 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11366 if (!can_replace[xx][yy])
11367 complete_replace = FALSE;
11370 if (!change->only_if_complete || complete_replace)
11372 boolean something_has_changed = FALSE;
11374 if (change->only_if_complete && change->use_random_replace &&
11375 RND(100) < change->random_percentage)
11378 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11380 int ex = x + xx - 1;
11381 int ey = y + yy - 1;
11382 int content_element;
11384 if (can_replace[xx][yy] && (!change->use_random_replace ||
11385 RND(100) < change->random_percentage))
11387 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11388 RemoveMovingField(ex, ey);
11390 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11392 content_element = change->target_content.e[xx][yy];
11393 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11394 ce_value, ce_score);
11396 CreateElementFromChange(ex, ey, target_element);
11398 something_has_changed = TRUE;
11400 /* for symmetry reasons, freeze newly created border elements */
11401 if (ex != x || ey != y)
11402 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11406 if (something_has_changed)
11408 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11409 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11415 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11416 ce_value, ce_score);
11418 if (element == EL_DIAGONAL_GROWING ||
11419 element == EL_DIAGONAL_SHRINKING)
11421 target_element = Store[x][y];
11423 Store[x][y] = EL_EMPTY;
11426 CreateElementFromChange(x, y, target_element);
11428 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11429 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11432 /* this uses direct change before indirect change */
11433 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11438 #if USE_NEW_DELAYED_ACTION
11440 static void HandleElementChange(int x, int y, int page)
11442 int element = MovingOrBlocked2Element(x, y);
11443 struct ElementInfo *ei = &element_info[element];
11444 struct ElementChangeInfo *change = &ei->change_page[page];
11445 boolean handle_action_before_change = FALSE;
11448 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11449 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11452 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11453 x, y, element, element_info[element].token_name);
11454 printf("HandleElementChange(): This should never happen!\n");
11459 /* this can happen with classic bombs on walkable, changing elements */
11460 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11463 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11464 ChangeDelay[x][y] = 0;
11470 if (ChangeDelay[x][y] == 0) /* initialize element change */
11472 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11474 if (change->can_change)
11477 /* !!! not clear why graphic animation should be reset at all here !!! */
11478 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11479 #if USE_GFX_RESET_WHEN_NOT_MOVING
11480 /* when a custom element is about to change (for example by change delay),
11481 do not reset graphic animation when the custom element is moving */
11482 if (!IS_MOVING(x, y))
11485 ResetGfxAnimation(x, y);
11486 ResetRandomAnimationValue(x, y);
11490 if (change->pre_change_function)
11491 change->pre_change_function(x, y);
11495 ChangeDelay[x][y]--;
11497 if (ChangeDelay[x][y] != 0) /* continue element change */
11499 if (change->can_change)
11501 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11503 if (IS_ANIMATED(graphic))
11504 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506 if (change->change_function)
11507 change->change_function(x, y);
11510 else /* finish element change */
11512 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11514 page = ChangePage[x][y];
11515 ChangePage[x][y] = -1;
11517 change = &ei->change_page[page];
11520 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11522 ChangeDelay[x][y] = 1; /* try change after next move step */
11523 ChangePage[x][y] = page; /* remember page to use for change */
11529 /* special case: set new level random seed before changing element */
11530 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11531 handle_action_before_change = TRUE;
11533 if (change->has_action && handle_action_before_change)
11534 ExecuteCustomElementAction(x, y, element, page);
11537 if (change->can_change)
11539 if (ChangeElement(x, y, element, page))
11541 if (change->post_change_function)
11542 change->post_change_function(x, y);
11546 if (change->has_action && !handle_action_before_change)
11547 ExecuteCustomElementAction(x, y, element, page);
11553 static void HandleElementChange(int x, int y, int page)
11555 int element = MovingOrBlocked2Element(x, y);
11556 struct ElementInfo *ei = &element_info[element];
11557 struct ElementChangeInfo *change = &ei->change_page[page];
11560 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11563 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11564 x, y, element, element_info[element].token_name);
11565 printf("HandleElementChange(): This should never happen!\n");
11570 /* this can happen with classic bombs on walkable, changing elements */
11571 if (!CAN_CHANGE(element))
11574 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11575 ChangeDelay[x][y] = 0;
11581 if (ChangeDelay[x][y] == 0) /* initialize element change */
11583 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11585 ResetGfxAnimation(x, y);
11586 ResetRandomAnimationValue(x, y);
11588 if (change->pre_change_function)
11589 change->pre_change_function(x, y);
11592 ChangeDelay[x][y]--;
11594 if (ChangeDelay[x][y] != 0) /* continue element change */
11596 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11598 if (IS_ANIMATED(graphic))
11599 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11601 if (change->change_function)
11602 change->change_function(x, y);
11604 else /* finish element change */
11606 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11608 page = ChangePage[x][y];
11609 ChangePage[x][y] = -1;
11611 change = &ei->change_page[page];
11614 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11616 ChangeDelay[x][y] = 1; /* try change after next move step */
11617 ChangePage[x][y] = page; /* remember page to use for change */
11622 if (ChangeElement(x, y, element, page))
11624 if (change->post_change_function)
11625 change->post_change_function(x, y);
11632 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11633 int trigger_element,
11635 int trigger_player,
11639 boolean change_done_any = FALSE;
11640 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11643 if (!(trigger_events[trigger_element][trigger_event]))
11647 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11648 trigger_event, recursion_loop_depth, recursion_loop_detected,
11649 recursion_loop_element, EL_NAME(recursion_loop_element));
11652 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11654 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11656 int element = EL_CUSTOM_START + i;
11657 boolean change_done = FALSE;
11660 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11661 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11664 for (p = 0; p < element_info[element].num_change_pages; p++)
11666 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11668 if (change->can_change_or_has_action &&
11669 change->has_event[trigger_event] &&
11670 change->trigger_side & trigger_side &&
11671 change->trigger_player & trigger_player &&
11672 change->trigger_page & trigger_page_bits &&
11673 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11675 change->actual_trigger_element = trigger_element;
11676 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11677 change->actual_trigger_player_bits = trigger_player;
11678 change->actual_trigger_side = trigger_side;
11679 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11680 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11683 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11684 element, EL_NAME(element), p);
11687 if ((change->can_change && !change_done) || change->has_action)
11691 SCAN_PLAYFIELD(x, y)
11693 if (Feld[x][y] == element)
11695 if (change->can_change && !change_done)
11697 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11698 /* if element already changed in this frame, not only prevent
11699 another element change (checked in ChangeElement()), but
11700 also prevent additional element actions for this element */
11702 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11703 !level.use_action_after_change_bug)
11708 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11709 element, EL_NAME(element), p);
11712 ChangeDelay[x][y] = 1;
11713 ChangeEvent[x][y] = trigger_event;
11715 HandleElementChange(x, y, p);
11717 #if USE_NEW_DELAYED_ACTION
11718 else if (change->has_action)
11720 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11721 /* if element already changed in this frame, not only prevent
11722 another element change (checked in ChangeElement()), but
11723 also prevent additional element actions for this element */
11725 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11726 !level.use_action_after_change_bug)
11732 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11733 element, EL_NAME(element), p);
11736 ExecuteCustomElementAction(x, y, element, p);
11737 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11740 if (change->has_action)
11742 ExecuteCustomElementAction(x, y, element, p);
11743 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11749 if (change->can_change)
11751 change_done = TRUE;
11752 change_done_any = TRUE;
11755 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11756 element, EL_NAME(element), p);
11765 RECURSION_LOOP_DETECTION_END();
11767 return change_done_any;
11770 static boolean CheckElementChangeExt(int x, int y,
11772 int trigger_element,
11774 int trigger_player,
11777 boolean change_done = FALSE;
11780 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11781 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11784 if (Feld[x][y] == EL_BLOCKED)
11786 Blocked2Moving(x, y, &x, &y);
11787 element = Feld[x][y];
11791 /* check if element has already changed */
11792 if (Feld[x][y] != element)
11795 /* check if element has already changed or is about to change after moving */
11796 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11797 Feld[x][y] != element) ||
11799 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11800 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11801 ChangePage[x][y] != -1)))
11806 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11807 trigger_event, recursion_loop_depth, recursion_loop_detected,
11808 recursion_loop_element, EL_NAME(recursion_loop_element));
11811 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11814 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11817 for (p = 0; p < element_info[element].num_change_pages; p++)
11819 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11821 /* check trigger element for all events where the element that is checked
11822 for changing interacts with a directly adjacent element -- this is
11823 different to element changes that affect other elements to change on the
11824 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11825 boolean check_trigger_element =
11826 (trigger_event == CE_TOUCHING_X ||
11827 trigger_event == CE_HITTING_X ||
11828 trigger_event == CE_HIT_BY_X ||
11830 /* this one was forgotten until 3.2.3 */
11831 trigger_event == CE_DIGGING_X);
11834 if (change->can_change_or_has_action &&
11835 change->has_event[trigger_event] &&
11836 change->trigger_side & trigger_side &&
11837 change->trigger_player & trigger_player &&
11838 (!check_trigger_element ||
11839 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11841 change->actual_trigger_element = trigger_element;
11842 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11843 change->actual_trigger_player_bits = trigger_player;
11844 change->actual_trigger_side = trigger_side;
11845 change->actual_trigger_ce_value = CustomValue[x][y];
11846 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11848 /* special case: trigger element not at (x,y) position for some events */
11849 if (check_trigger_element)
11861 { 0, 0 }, { 0, 0 }, { 0, 0 },
11865 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11866 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11868 change->actual_trigger_ce_value = CustomValue[xx][yy];
11869 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11872 if (change->can_change && !change_done)
11874 ChangeDelay[x][y] = 1;
11875 ChangeEvent[x][y] = trigger_event;
11877 HandleElementChange(x, y, p);
11879 change_done = TRUE;
11881 #if USE_NEW_DELAYED_ACTION
11882 else if (change->has_action)
11884 ExecuteCustomElementAction(x, y, element, p);
11885 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11888 if (change->has_action)
11890 ExecuteCustomElementAction(x, y, element, p);
11891 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11897 RECURSION_LOOP_DETECTION_END();
11899 return change_done;
11902 static void PlayPlayerSound(struct PlayerInfo *player)
11904 int jx = player->jx, jy = player->jy;
11905 int sound_element = player->artwork_element;
11906 int last_action = player->last_action_waiting;
11907 int action = player->action_waiting;
11909 if (player->is_waiting)
11911 if (action != last_action)
11912 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11914 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11918 if (action != last_action)
11919 StopSound(element_info[sound_element].sound[last_action]);
11921 if (last_action == ACTION_SLEEPING)
11922 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11926 static void PlayAllPlayersSound()
11930 for (i = 0; i < MAX_PLAYERS; i++)
11931 if (stored_player[i].active)
11932 PlayPlayerSound(&stored_player[i]);
11935 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11937 boolean last_waiting = player->is_waiting;
11938 int move_dir = player->MovDir;
11940 player->dir_waiting = move_dir;
11941 player->last_action_waiting = player->action_waiting;
11945 if (!last_waiting) /* not waiting -> waiting */
11947 player->is_waiting = TRUE;
11949 player->frame_counter_bored =
11951 game.player_boring_delay_fixed +
11952 GetSimpleRandom(game.player_boring_delay_random);
11953 player->frame_counter_sleeping =
11955 game.player_sleeping_delay_fixed +
11956 GetSimpleRandom(game.player_sleeping_delay_random);
11958 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11961 if (game.player_sleeping_delay_fixed +
11962 game.player_sleeping_delay_random > 0 &&
11963 player->anim_delay_counter == 0 &&
11964 player->post_delay_counter == 0 &&
11965 FrameCounter >= player->frame_counter_sleeping)
11966 player->is_sleeping = TRUE;
11967 else if (game.player_boring_delay_fixed +
11968 game.player_boring_delay_random > 0 &&
11969 FrameCounter >= player->frame_counter_bored)
11970 player->is_bored = TRUE;
11972 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11973 player->is_bored ? ACTION_BORING :
11976 if (player->is_sleeping && player->use_murphy)
11978 /* special case for sleeping Murphy when leaning against non-free tile */
11980 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11981 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11982 !IS_MOVING(player->jx - 1, player->jy)))
11983 move_dir = MV_LEFT;
11984 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11985 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11986 !IS_MOVING(player->jx + 1, player->jy)))
11987 move_dir = MV_RIGHT;
11989 player->is_sleeping = FALSE;
11991 player->dir_waiting = move_dir;
11994 if (player->is_sleeping)
11996 if (player->num_special_action_sleeping > 0)
11998 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12000 int last_special_action = player->special_action_sleeping;
12001 int num_special_action = player->num_special_action_sleeping;
12002 int special_action =
12003 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
12004 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
12005 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
12006 last_special_action + 1 : ACTION_SLEEPING);
12007 int special_graphic =
12008 el_act_dir2img(player->artwork_element, special_action, move_dir);
12010 player->anim_delay_counter =
12011 graphic_info[special_graphic].anim_delay_fixed +
12012 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12013 player->post_delay_counter =
12014 graphic_info[special_graphic].post_delay_fixed +
12015 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12017 player->special_action_sleeping = special_action;
12020 if (player->anim_delay_counter > 0)
12022 player->action_waiting = player->special_action_sleeping;
12023 player->anim_delay_counter--;
12025 else if (player->post_delay_counter > 0)
12027 player->post_delay_counter--;
12031 else if (player->is_bored)
12033 if (player->num_special_action_bored > 0)
12035 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
12037 int special_action =
12038 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
12039 int special_graphic =
12040 el_act_dir2img(player->artwork_element, special_action, move_dir);
12042 player->anim_delay_counter =
12043 graphic_info[special_graphic].anim_delay_fixed +
12044 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12045 player->post_delay_counter =
12046 graphic_info[special_graphic].post_delay_fixed +
12047 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12049 player->special_action_bored = special_action;
12052 if (player->anim_delay_counter > 0)
12054 player->action_waiting = player->special_action_bored;
12055 player->anim_delay_counter--;
12057 else if (player->post_delay_counter > 0)
12059 player->post_delay_counter--;
12064 else if (last_waiting) /* waiting -> not waiting */
12066 player->is_waiting = FALSE;
12067 player->is_bored = FALSE;
12068 player->is_sleeping = FALSE;
12070 player->frame_counter_bored = -1;
12071 player->frame_counter_sleeping = -1;
12073 player->anim_delay_counter = 0;
12074 player->post_delay_counter = 0;
12076 player->dir_waiting = player->MovDir;
12077 player->action_waiting = ACTION_DEFAULT;
12079 player->special_action_bored = ACTION_DEFAULT;
12080 player->special_action_sleeping = ACTION_DEFAULT;
12084 static void CheckSingleStepMode(struct PlayerInfo *player)
12086 if (tape.single_step && tape.recording && !tape.pausing)
12088 /* as it is called "single step mode", just return to pause mode when the
12089 player stopped moving after one tile (or never starts moving at all) */
12090 if (!player->is_moving && !player->is_pushing)
12092 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12093 SnapField(player, 0, 0); /* stop snapping */
12098 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12100 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12101 int left = player_action & JOY_LEFT;
12102 int right = player_action & JOY_RIGHT;
12103 int up = player_action & JOY_UP;
12104 int down = player_action & JOY_DOWN;
12105 int button1 = player_action & JOY_BUTTON_1;
12106 int button2 = player_action & JOY_BUTTON_2;
12107 int dx = (left ? -1 : right ? 1 : 0);
12108 int dy = (up ? -1 : down ? 1 : 0);
12110 if (!player->active || tape.pausing)
12116 snapped = SnapField(player, dx, dy);
12120 dropped = DropElement(player);
12122 moved = MovePlayer(player, dx, dy);
12125 CheckSingleStepMode(player);
12127 SetPlayerWaiting(player, FALSE);
12129 return player_action;
12133 /* no actions for this player (no input at player's configured device) */
12135 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12136 SnapField(player, 0, 0);
12137 CheckGravityMovementWhenNotMoving(player);
12139 if (player->MovPos == 0)
12140 SetPlayerWaiting(player, TRUE);
12142 if (player->MovPos == 0) /* needed for tape.playing */
12143 player->is_moving = FALSE;
12145 player->is_dropping = FALSE;
12146 player->is_dropping_pressed = FALSE;
12147 player->drop_pressed_delay = 0;
12149 CheckSingleStepMode(player);
12155 static void CheckLevelTime()
12159 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12160 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12162 if (level.native_em_level->lev->home == 0) /* all players at home */
12164 PlayerWins(local_player);
12166 AllPlayersGone = TRUE;
12168 level.native_em_level->lev->home = -1;
12171 if (level.native_em_level->ply[0]->alive == 0 &&
12172 level.native_em_level->ply[1]->alive == 0 &&
12173 level.native_em_level->ply[2]->alive == 0 &&
12174 level.native_em_level->ply[3]->alive == 0) /* all dead */
12175 AllPlayersGone = TRUE;
12177 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12179 if (game_sp.LevelSolved &&
12180 !game_sp.GameOver) /* game won */
12182 PlayerWins(local_player);
12184 game_sp.GameOver = TRUE;
12186 AllPlayersGone = TRUE;
12189 if (game_sp.GameOver) /* game lost */
12190 AllPlayersGone = TRUE;
12193 if (TimeFrames >= FRAMES_PER_SECOND)
12198 for (i = 0; i < MAX_PLAYERS; i++)
12200 struct PlayerInfo *player = &stored_player[i];
12202 if (SHIELD_ON(player))
12204 player->shield_normal_time_left--;
12206 if (player->shield_deadly_time_left > 0)
12207 player->shield_deadly_time_left--;
12211 if (!local_player->LevelSolved && !level.use_step_counter)
12219 if (TimeLeft <= 10 && setup.time_limit)
12220 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12223 /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
12224 is reset from other values in UpdateGameDoorValues() -- FIX THIS */
12226 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12228 DisplayGameControlValues();
12230 DrawGameValue_Time(TimeLeft);
12233 if (!TimeLeft && setup.time_limit)
12235 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12236 level.native_em_level->lev->killed_out_of_time = TRUE;
12238 for (i = 0; i < MAX_PLAYERS; i++)
12239 KillPlayer(&stored_player[i]);
12243 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12245 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12247 DisplayGameControlValues();
12250 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12251 DrawGameValue_Time(TimePlayed);
12254 level.native_em_level->lev->time =
12255 (game.no_time_limit ? TimePlayed : TimeLeft);
12258 if (tape.recording || tape.playing)
12259 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12263 UpdateAndDisplayGameControlValues();
12265 UpdateGameDoorValues();
12266 DrawGameDoorValues();
12270 void AdvanceFrameAndPlayerCounters(int player_nr)
12274 /* advance frame counters (global frame counter and time frame counter) */
12278 /* advance player counters (counters for move delay, move animation etc.) */
12279 for (i = 0; i < MAX_PLAYERS; i++)
12281 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12282 int move_delay_value = stored_player[i].move_delay_value;
12283 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12285 if (!advance_player_counters) /* not all players may be affected */
12288 #if USE_NEW_PLAYER_ANIM
12289 if (move_frames == 0) /* less than one move per game frame */
12291 int stepsize = TILEX / move_delay_value;
12292 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12293 int count = (stored_player[i].is_moving ?
12294 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12296 if (count % delay == 0)
12301 stored_player[i].Frame += move_frames;
12303 if (stored_player[i].MovPos != 0)
12304 stored_player[i].StepFrame += move_frames;
12306 if (stored_player[i].move_delay > 0)
12307 stored_player[i].move_delay--;
12309 /* due to bugs in previous versions, counter must count up, not down */
12310 if (stored_player[i].push_delay != -1)
12311 stored_player[i].push_delay++;
12313 if (stored_player[i].drop_delay > 0)
12314 stored_player[i].drop_delay--;
12316 if (stored_player[i].is_dropping_pressed)
12317 stored_player[i].drop_pressed_delay++;
12321 void StartGameActions(boolean init_network_game, boolean record_tape,
12324 unsigned int new_random_seed = InitRND(random_seed);
12327 TapeStartRecording(new_random_seed);
12329 #if defined(NETWORK_AVALIABLE)
12330 if (init_network_game)
12332 SendToServer_StartPlaying();
12343 static unsigned int game_frame_delay = 0;
12344 unsigned int game_frame_delay_value;
12345 byte *recorded_player_action;
12346 byte summarized_player_action = 0;
12347 byte tape_action[MAX_PLAYERS];
12350 /* detect endless loops, caused by custom element programming */
12351 if (recursion_loop_detected && recursion_loop_depth == 0)
12353 char *message = getStringCat3("Internal Error ! Element ",
12354 EL_NAME(recursion_loop_element),
12355 " caused endless loop ! Quit the game ?");
12357 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12358 EL_NAME(recursion_loop_element));
12360 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12362 recursion_loop_detected = FALSE; /* if game should be continued */
12369 if (game.restart_level)
12370 StartGameActions(options.network, setup.autorecord, level.random_seed);
12372 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12373 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12375 if (level.native_em_level->lev->home == 0) /* all players at home */
12377 PlayerWins(local_player);
12379 AllPlayersGone = TRUE;
12381 level.native_em_level->lev->home = -1;
12384 if (level.native_em_level->ply[0]->alive == 0 &&
12385 level.native_em_level->ply[1]->alive == 0 &&
12386 level.native_em_level->ply[2]->alive == 0 &&
12387 level.native_em_level->ply[3]->alive == 0) /* all dead */
12388 AllPlayersGone = TRUE;
12390 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12392 if (game_sp.LevelSolved &&
12393 !game_sp.GameOver) /* game won */
12395 PlayerWins(local_player);
12397 game_sp.GameOver = TRUE;
12399 AllPlayersGone = TRUE;
12402 if (game_sp.GameOver) /* game lost */
12403 AllPlayersGone = TRUE;
12406 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12409 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12412 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12415 game_frame_delay_value =
12416 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12418 if (tape.playing && tape.warp_forward && !tape.pausing)
12419 game_frame_delay_value = 0;
12421 /* ---------- main game synchronization point ---------- */
12423 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12425 if (network_playing && !network_player_action_received)
12427 /* try to get network player actions in time */
12429 #if defined(NETWORK_AVALIABLE)
12430 /* last chance to get network player actions without main loop delay */
12431 HandleNetworking();
12434 /* game was quit by network peer */
12435 if (game_status != GAME_MODE_PLAYING)
12438 if (!network_player_action_received)
12439 return; /* failed to get network player actions in time */
12441 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12447 /* at this point we know that we really continue executing the game */
12449 network_player_action_received = FALSE;
12451 /* when playing tape, read previously recorded player input from tape data */
12452 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12455 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12460 if (tape.set_centered_player)
12462 game.centered_player_nr_next = tape.centered_player_nr_next;
12463 game.set_centered_player = TRUE;
12466 for (i = 0; i < MAX_PLAYERS; i++)
12468 summarized_player_action |= stored_player[i].action;
12470 if (!network_playing)
12471 stored_player[i].effective_action = stored_player[i].action;
12474 #if defined(NETWORK_AVALIABLE)
12475 if (network_playing)
12476 SendToServer_MovePlayer(summarized_player_action);
12479 if (!options.network && !setup.team_mode)
12480 local_player->effective_action = summarized_player_action;
12482 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12484 for (i = 0; i < MAX_PLAYERS; i++)
12485 stored_player[i].effective_action =
12486 (i == game.centered_player_nr ? summarized_player_action : 0);
12489 if (recorded_player_action != NULL)
12490 for (i = 0; i < MAX_PLAYERS; i++)
12491 stored_player[i].effective_action = recorded_player_action[i];
12493 for (i = 0; i < MAX_PLAYERS; i++)
12495 tape_action[i] = stored_player[i].effective_action;
12497 /* (this can only happen in the R'n'D game engine) */
12498 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12499 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12502 /* only record actions from input devices, but not programmed actions */
12503 if (tape.recording)
12504 TapeRecordAction(tape_action);
12506 #if USE_NEW_PLAYER_ASSIGNMENTS
12508 byte mapped_action[MAX_PLAYERS];
12510 for (i = 0; i < MAX_PLAYERS; i++)
12511 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12513 for (i = 0; i < MAX_PLAYERS; i++)
12514 stored_player[i].effective_action = mapped_action[i];
12518 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12520 GameActions_EM_Main();
12522 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12524 GameActions_SP_Main();
12532 void GameActions_EM_Main()
12534 byte effective_action[MAX_PLAYERS];
12535 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12538 for (i = 0; i < MAX_PLAYERS; i++)
12539 effective_action[i] = stored_player[i].effective_action;
12541 GameActions_EM(effective_action, warp_mode);
12545 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12548 void GameActions_SP_Main()
12550 byte effective_action[MAX_PLAYERS];
12551 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12554 for (i = 0; i < MAX_PLAYERS; i++)
12555 effective_action[i] = stored_player[i].effective_action;
12557 GameActions_SP(effective_action, warp_mode);
12561 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12564 void GameActions_RND()
12566 int magic_wall_x = 0, magic_wall_y = 0;
12567 int i, x, y, element, graphic;
12569 InitPlayfieldScanModeVars();
12571 #if USE_ONE_MORE_CHANGE_PER_FRAME
12572 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12574 SCAN_PLAYFIELD(x, y)
12576 ChangeCount[x][y] = 0;
12577 ChangeEvent[x][y] = -1;
12582 if (game.set_centered_player)
12584 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12586 /* switching to "all players" only possible if all players fit to screen */
12587 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12589 game.centered_player_nr_next = game.centered_player_nr;
12590 game.set_centered_player = FALSE;
12593 /* do not switch focus to non-existing (or non-active) player */
12594 if (game.centered_player_nr_next >= 0 &&
12595 !stored_player[game.centered_player_nr_next].active)
12597 game.centered_player_nr_next = game.centered_player_nr;
12598 game.set_centered_player = FALSE;
12602 if (game.set_centered_player &&
12603 ScreenMovPos == 0) /* screen currently aligned at tile position */
12607 if (game.centered_player_nr_next == -1)
12609 setScreenCenteredToAllPlayers(&sx, &sy);
12613 sx = stored_player[game.centered_player_nr_next].jx;
12614 sy = stored_player[game.centered_player_nr_next].jy;
12617 game.centered_player_nr = game.centered_player_nr_next;
12618 game.set_centered_player = FALSE;
12620 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12621 DrawGameDoorValues();
12624 for (i = 0; i < MAX_PLAYERS; i++)
12626 int actual_player_action = stored_player[i].effective_action;
12629 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12630 - rnd_equinox_tetrachloride 048
12631 - rnd_equinox_tetrachloride_ii 096
12632 - rnd_emanuel_schmieg 002
12633 - doctor_sloan_ww 001, 020
12635 if (stored_player[i].MovPos == 0)
12636 CheckGravityMovement(&stored_player[i]);
12639 /* overwrite programmed action with tape action */
12640 if (stored_player[i].programmed_action)
12641 actual_player_action = stored_player[i].programmed_action;
12643 PlayerActions(&stored_player[i], actual_player_action);
12645 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12648 ScrollScreen(NULL, SCROLL_GO_ON);
12650 /* for backwards compatibility, the following code emulates a fixed bug that
12651 occured when pushing elements (causing elements that just made their last
12652 pushing step to already (if possible) make their first falling step in the
12653 same game frame, which is bad); this code is also needed to use the famous
12654 "spring push bug" which is used in older levels and might be wanted to be
12655 used also in newer levels, but in this case the buggy pushing code is only
12656 affecting the "spring" element and no other elements */
12658 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12660 for (i = 0; i < MAX_PLAYERS; i++)
12662 struct PlayerInfo *player = &stored_player[i];
12663 int x = player->jx;
12664 int y = player->jy;
12666 if (player->active && player->is_pushing && player->is_moving &&
12668 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12669 Feld[x][y] == EL_SPRING))
12671 ContinueMoving(x, y);
12673 /* continue moving after pushing (this is actually a bug) */
12674 if (!IS_MOVING(x, y))
12675 Stop[x][y] = FALSE;
12681 debug_print_timestamp(0, "start main loop profiling");
12684 SCAN_PLAYFIELD(x, y)
12686 ChangeCount[x][y] = 0;
12687 ChangeEvent[x][y] = -1;
12689 /* this must be handled before main playfield loop */
12690 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12693 if (MovDelay[x][y] <= 0)
12697 #if USE_NEW_SNAP_DELAY
12698 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12701 if (MovDelay[x][y] <= 0)
12704 TEST_DrawLevelField(x, y);
12706 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12712 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12714 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12715 printf("GameActions(): This should never happen!\n");
12717 ChangePage[x][y] = -1;
12721 Stop[x][y] = FALSE;
12722 if (WasJustMoving[x][y] > 0)
12723 WasJustMoving[x][y]--;
12724 if (WasJustFalling[x][y] > 0)
12725 WasJustFalling[x][y]--;
12726 if (CheckCollision[x][y] > 0)
12727 CheckCollision[x][y]--;
12728 if (CheckImpact[x][y] > 0)
12729 CheckImpact[x][y]--;
12733 /* reset finished pushing action (not done in ContinueMoving() to allow
12734 continuous pushing animation for elements with zero push delay) */
12735 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12737 ResetGfxAnimation(x, y);
12738 TEST_DrawLevelField(x, y);
12742 if (IS_BLOCKED(x, y))
12746 Blocked2Moving(x, y, &oldx, &oldy);
12747 if (!IS_MOVING(oldx, oldy))
12749 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12750 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12751 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12752 printf("GameActions(): This should never happen!\n");
12759 debug_print_timestamp(0, "- time for pre-main loop:");
12762 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12763 SCAN_PLAYFIELD(x, y)
12765 element = Feld[x][y];
12766 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12771 int element2 = element;
12772 int graphic2 = graphic;
12774 int element2 = Feld[x][y];
12775 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12777 int last_gfx_frame = GfxFrame[x][y];
12779 if (graphic_info[graphic2].anim_global_sync)
12780 GfxFrame[x][y] = FrameCounter;
12781 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12782 GfxFrame[x][y] = CustomValue[x][y];
12783 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12784 GfxFrame[x][y] = element_info[element2].collect_score;
12785 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12786 GfxFrame[x][y] = ChangeDelay[x][y];
12788 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12789 DrawLevelGraphicAnimation(x, y, graphic2);
12792 ResetGfxFrame(x, y, TRUE);
12796 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12797 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12798 ResetRandomAnimationValue(x, y);
12802 SetRandomAnimationValue(x, y);
12806 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12809 #endif // -------------------- !!! TEST ONLY !!! --------------------
12812 debug_print_timestamp(0, "- time for TEST loop: -->");
12815 SCAN_PLAYFIELD(x, y)
12817 element = Feld[x][y];
12818 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12820 ResetGfxFrame(x, y, TRUE);
12822 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12823 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12824 ResetRandomAnimationValue(x, y);
12826 SetRandomAnimationValue(x, y);
12828 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12830 if (IS_INACTIVE(element))
12832 if (IS_ANIMATED(graphic))
12833 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12838 /* this may take place after moving, so 'element' may have changed */
12839 if (IS_CHANGING(x, y) &&
12840 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12842 int page = element_info[element].event_page_nr[CE_DELAY];
12845 HandleElementChange(x, y, page);
12847 if (CAN_CHANGE(element))
12848 HandleElementChange(x, y, page);
12850 if (HAS_ACTION(element))
12851 ExecuteCustomElementAction(x, y, element, page);
12854 element = Feld[x][y];
12855 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12858 #if 0 // ---------------------------------------------------------------------
12860 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12864 element = Feld[x][y];
12865 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12867 if (IS_ANIMATED(graphic) &&
12868 !IS_MOVING(x, y) &&
12870 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12872 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12873 TEST_DrawTwinkleOnField(x, y);
12875 else if (IS_MOVING(x, y))
12876 ContinueMoving(x, y);
12883 case EL_EM_EXIT_OPEN:
12884 case EL_SP_EXIT_OPEN:
12885 case EL_STEEL_EXIT_OPEN:
12886 case EL_EM_STEEL_EXIT_OPEN:
12887 case EL_SP_TERMINAL:
12888 case EL_SP_TERMINAL_ACTIVE:
12889 case EL_EXTRA_TIME:
12890 case EL_SHIELD_NORMAL:
12891 case EL_SHIELD_DEADLY:
12892 if (IS_ANIMATED(graphic))
12893 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12896 case EL_DYNAMITE_ACTIVE:
12897 case EL_EM_DYNAMITE_ACTIVE:
12898 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12899 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12900 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12901 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12902 case EL_SP_DISK_RED_ACTIVE:
12903 CheckDynamite(x, y);
12906 case EL_AMOEBA_GROWING:
12907 AmoebeWaechst(x, y);
12910 case EL_AMOEBA_SHRINKING:
12911 AmoebaDisappearing(x, y);
12914 #if !USE_NEW_AMOEBA_CODE
12915 case EL_AMOEBA_WET:
12916 case EL_AMOEBA_DRY:
12917 case EL_AMOEBA_FULL:
12919 case EL_EMC_DRIPPER:
12920 AmoebeAbleger(x, y);
12924 case EL_GAME_OF_LIFE:
12929 case EL_EXIT_CLOSED:
12933 case EL_EM_EXIT_CLOSED:
12937 case EL_STEEL_EXIT_CLOSED:
12938 CheckExitSteel(x, y);
12941 case EL_EM_STEEL_EXIT_CLOSED:
12942 CheckExitSteelEM(x, y);
12945 case EL_SP_EXIT_CLOSED:
12949 case EL_EXPANDABLE_WALL_GROWING:
12950 case EL_EXPANDABLE_STEELWALL_GROWING:
12951 MauerWaechst(x, y);
12954 case EL_EXPANDABLE_WALL:
12955 case EL_EXPANDABLE_WALL_HORIZONTAL:
12956 case EL_EXPANDABLE_WALL_VERTICAL:
12957 case EL_EXPANDABLE_WALL_ANY:
12958 case EL_BD_EXPANDABLE_WALL:
12959 MauerAbleger(x, y);
12962 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12963 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12964 case EL_EXPANDABLE_STEELWALL_ANY:
12965 MauerAblegerStahl(x, y);
12969 CheckForDragon(x, y);
12975 case EL_ELEMENT_SNAPPING:
12976 case EL_DIAGONAL_SHRINKING:
12977 case EL_DIAGONAL_GROWING:
12980 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12982 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12987 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12988 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12993 #else // ---------------------------------------------------------------------
12995 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12999 element = Feld[x][y];
13000 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
13002 if (IS_ANIMATED(graphic) &&
13003 !IS_MOVING(x, y) &&
13005 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13007 if (IS_GEM(element) || element == EL_SP_INFOTRON)
13008 TEST_DrawTwinkleOnField(x, y);
13010 else if ((element == EL_ACID ||
13011 element == EL_EXIT_OPEN ||
13012 element == EL_EM_EXIT_OPEN ||
13013 element == EL_SP_EXIT_OPEN ||
13014 element == EL_STEEL_EXIT_OPEN ||
13015 element == EL_EM_STEEL_EXIT_OPEN ||
13016 element == EL_SP_TERMINAL ||
13017 element == EL_SP_TERMINAL_ACTIVE ||
13018 element == EL_EXTRA_TIME ||
13019 element == EL_SHIELD_NORMAL ||
13020 element == EL_SHIELD_DEADLY) &&
13021 IS_ANIMATED(graphic))
13022 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13023 else if (IS_MOVING(x, y))
13024 ContinueMoving(x, y);
13025 else if (IS_ACTIVE_BOMB(element))
13026 CheckDynamite(x, y);
13027 else if (element == EL_AMOEBA_GROWING)
13028 AmoebeWaechst(x, y);
13029 else if (element == EL_AMOEBA_SHRINKING)
13030 AmoebaDisappearing(x, y);
13032 #if !USE_NEW_AMOEBA_CODE
13033 else if (IS_AMOEBALIVE(element))
13034 AmoebeAbleger(x, y);
13037 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
13039 else if (element == EL_EXIT_CLOSED)
13041 else if (element == EL_EM_EXIT_CLOSED)
13043 else if (element == EL_STEEL_EXIT_CLOSED)
13044 CheckExitSteel(x, y);
13045 else if (element == EL_EM_STEEL_EXIT_CLOSED)
13046 CheckExitSteelEM(x, y);
13047 else if (element == EL_SP_EXIT_CLOSED)
13049 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13050 element == EL_EXPANDABLE_STEELWALL_GROWING)
13051 MauerWaechst(x, y);
13052 else if (element == EL_EXPANDABLE_WALL ||
13053 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13054 element == EL_EXPANDABLE_WALL_VERTICAL ||
13055 element == EL_EXPANDABLE_WALL_ANY ||
13056 element == EL_BD_EXPANDABLE_WALL)
13057 MauerAbleger(x, y);
13058 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13059 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13060 element == EL_EXPANDABLE_STEELWALL_ANY)
13061 MauerAblegerStahl(x, y);
13062 else if (element == EL_FLAMES)
13063 CheckForDragon(x, y);
13064 else if (element == EL_EXPLOSION)
13065 ; /* drawing of correct explosion animation is handled separately */
13066 else if (element == EL_ELEMENT_SNAPPING ||
13067 element == EL_DIAGONAL_SHRINKING ||
13068 element == EL_DIAGONAL_GROWING)
13070 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13072 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13074 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13075 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13077 #endif // ---------------------------------------------------------------------
13079 if (IS_BELT_ACTIVE(element))
13080 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13082 if (game.magic_wall_active)
13084 int jx = local_player->jx, jy = local_player->jy;
13086 /* play the element sound at the position nearest to the player */
13087 if ((element == EL_MAGIC_WALL_FULL ||
13088 element == EL_MAGIC_WALL_ACTIVE ||
13089 element == EL_MAGIC_WALL_EMPTYING ||
13090 element == EL_BD_MAGIC_WALL_FULL ||
13091 element == EL_BD_MAGIC_WALL_ACTIVE ||
13092 element == EL_BD_MAGIC_WALL_EMPTYING ||
13093 element == EL_DC_MAGIC_WALL_FULL ||
13094 element == EL_DC_MAGIC_WALL_ACTIVE ||
13095 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13096 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13105 debug_print_timestamp(0, "- time for MAIN loop: -->");
13108 #if USE_NEW_AMOEBA_CODE
13109 /* new experimental amoeba growth stuff */
13110 if (!(FrameCounter % 8))
13112 static unsigned int random = 1684108901;
13114 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13116 x = RND(lev_fieldx);
13117 y = RND(lev_fieldy);
13118 element = Feld[x][y];
13120 if (!IS_PLAYER(x,y) &&
13121 (element == EL_EMPTY ||
13122 CAN_GROW_INTO(element) ||
13123 element == EL_QUICKSAND_EMPTY ||
13124 element == EL_QUICKSAND_FAST_EMPTY ||
13125 element == EL_ACID_SPLASH_LEFT ||
13126 element == EL_ACID_SPLASH_RIGHT))
13128 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13129 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13130 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13131 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13132 Feld[x][y] = EL_AMOEBA_DROP;
13135 random = random * 129 + 1;
13141 if (game.explosions_delayed)
13144 game.explosions_delayed = FALSE;
13146 SCAN_PLAYFIELD(x, y)
13148 element = Feld[x][y];
13150 if (ExplodeField[x][y])
13151 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13152 else if (element == EL_EXPLOSION)
13153 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13155 ExplodeField[x][y] = EX_TYPE_NONE;
13158 game.explosions_delayed = TRUE;
13161 if (game.magic_wall_active)
13163 if (!(game.magic_wall_time_left % 4))
13165 int element = Feld[magic_wall_x][magic_wall_y];
13167 if (element == EL_BD_MAGIC_WALL_FULL ||
13168 element == EL_BD_MAGIC_WALL_ACTIVE ||
13169 element == EL_BD_MAGIC_WALL_EMPTYING)
13170 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13171 else if (element == EL_DC_MAGIC_WALL_FULL ||
13172 element == EL_DC_MAGIC_WALL_ACTIVE ||
13173 element == EL_DC_MAGIC_WALL_EMPTYING)
13174 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13176 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13179 if (game.magic_wall_time_left > 0)
13181 game.magic_wall_time_left--;
13183 if (!game.magic_wall_time_left)
13185 SCAN_PLAYFIELD(x, y)
13187 element = Feld[x][y];
13189 if (element == EL_MAGIC_WALL_ACTIVE ||
13190 element == EL_MAGIC_WALL_FULL)
13192 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13193 TEST_DrawLevelField(x, y);
13195 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13196 element == EL_BD_MAGIC_WALL_FULL)
13198 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13199 TEST_DrawLevelField(x, y);
13201 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13202 element == EL_DC_MAGIC_WALL_FULL)
13204 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13205 TEST_DrawLevelField(x, y);
13209 game.magic_wall_active = FALSE;
13214 if (game.light_time_left > 0)
13216 game.light_time_left--;
13218 if (game.light_time_left == 0)
13219 RedrawAllLightSwitchesAndInvisibleElements();
13222 if (game.timegate_time_left > 0)
13224 game.timegate_time_left--;
13226 if (game.timegate_time_left == 0)
13227 CloseAllOpenTimegates();
13230 if (game.lenses_time_left > 0)
13232 game.lenses_time_left--;
13234 if (game.lenses_time_left == 0)
13235 RedrawAllInvisibleElementsForLenses();
13238 if (game.magnify_time_left > 0)
13240 game.magnify_time_left--;
13242 if (game.magnify_time_left == 0)
13243 RedrawAllInvisibleElementsForMagnifier();
13246 for (i = 0; i < MAX_PLAYERS; i++)
13248 struct PlayerInfo *player = &stored_player[i];
13250 if (SHIELD_ON(player))
13252 if (player->shield_deadly_time_left)
13253 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13254 else if (player->shield_normal_time_left)
13255 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13259 #if USE_DELAYED_GFX_REDRAW
13260 SCAN_PLAYFIELD(x, y)
13263 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13265 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13266 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13269 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13270 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13272 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13273 DrawLevelField(x, y);
13275 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13276 DrawLevelFieldCrumbled(x, y);
13278 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13279 DrawLevelFieldCrumbledNeighbours(x, y);
13281 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13282 DrawTwinkleOnField(x, y);
13285 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13292 PlayAllPlayersSound();
13294 if (options.debug) /* calculate frames per second */
13296 static unsigned int fps_counter = 0;
13297 static int fps_frames = 0;
13298 unsigned int fps_delay_ms = Counter() - fps_counter;
13302 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13304 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13307 fps_counter = Counter();
13310 redraw_mask |= REDRAW_FPS;
13313 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13315 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13317 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13319 local_player->show_envelope = 0;
13323 debug_print_timestamp(0, "stop main loop profiling ");
13324 printf("----------------------------------------------------------\n");
13327 /* use random number generator in every frame to make it less predictable */
13328 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13332 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13334 int min_x = x, min_y = y, max_x = x, max_y = y;
13337 for (i = 0; i < MAX_PLAYERS; i++)
13339 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13341 if (!stored_player[i].active || &stored_player[i] == player)
13344 min_x = MIN(min_x, jx);
13345 min_y = MIN(min_y, jy);
13346 max_x = MAX(max_x, jx);
13347 max_y = MAX(max_y, jy);
13350 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13353 static boolean AllPlayersInVisibleScreen()
13357 for (i = 0; i < MAX_PLAYERS; i++)
13359 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13361 if (!stored_player[i].active)
13364 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13371 void ScrollLevel(int dx, int dy)
13374 /* (directly solved in BlitBitmap() now) */
13375 static Bitmap *bitmap_db_field2 = NULL;
13376 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13383 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13384 /* only horizontal XOR vertical scroll direction allowed */
13385 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13390 /* (directly solved in BlitBitmap() now) */
13391 if (bitmap_db_field2 == NULL)
13392 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13394 /* needed when blitting directly to same bitmap -- should not be needed with
13395 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13396 BlitBitmap(drawto_field, bitmap_db_field2,
13397 FX + TILEX * (dx == -1) - softscroll_offset,
13398 FY + TILEY * (dy == -1) - softscroll_offset,
13399 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13400 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13401 FX + TILEX * (dx == 1) - softscroll_offset,
13402 FY + TILEY * (dy == 1) - softscroll_offset);
13403 BlitBitmap(bitmap_db_field2, drawto_field,
13404 FX + TILEX * (dx == 1) - softscroll_offset,
13405 FY + TILEY * (dy == 1) - softscroll_offset,
13406 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13407 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13408 FX + TILEX * (dx == 1) - softscroll_offset,
13409 FY + TILEY * (dy == 1) - softscroll_offset);
13414 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13415 int xsize = (BX2 - BX1 + 1);
13416 int ysize = (BY2 - BY1 + 1);
13417 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13418 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13419 int step = (start < end ? +1 : -1);
13421 for (i = start; i != end; i += step)
13423 BlitBitmap(drawto_field, drawto_field,
13424 FX + TILEX * (dx != 0 ? i + step : 0),
13425 FY + TILEY * (dy != 0 ? i + step : 0),
13426 TILEX * (dx != 0 ? 1 : xsize),
13427 TILEY * (dy != 0 ? 1 : ysize),
13428 FX + TILEX * (dx != 0 ? i : 0),
13429 FY + TILEY * (dy != 0 ? i : 0));
13436 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13438 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13442 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13444 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13449 BlitBitmap(drawto_field, drawto_field,
13450 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13451 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13452 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13453 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13454 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13455 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13457 BlitBitmap(drawto_field, drawto_field,
13458 FX + TILEX * (dx == -1) - softscroll_offset,
13459 FY + TILEY * (dy == -1) - softscroll_offset,
13460 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13461 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13462 FX + TILEX * (dx == 1) - softscroll_offset,
13463 FY + TILEY * (dy == 1) - softscroll_offset);
13471 x = (dx == 1 ? BX1 : BX2);
13472 for (y = BY1; y <= BY2; y++)
13473 DrawScreenField(x, y);
13478 y = (dy == 1 ? BY1 : BY2);
13479 for (x = BX1; x <= BX2; x++)
13480 DrawScreenField(x, y);
13483 redraw_mask |= REDRAW_FIELD;
13486 static boolean canFallDown(struct PlayerInfo *player)
13488 int jx = player->jx, jy = player->jy;
13490 return (IN_LEV_FIELD(jx, jy + 1) &&
13491 (IS_FREE(jx, jy + 1) ||
13492 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13493 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13494 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13497 static boolean canPassField(int x, int y, int move_dir)
13499 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13500 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13501 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13502 int nextx = x + dx;
13503 int nexty = y + dy;
13504 int element = Feld[x][y];
13506 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13507 !CAN_MOVE(element) &&
13508 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13509 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13510 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13513 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13515 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13516 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13517 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13521 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13522 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13523 (IS_DIGGABLE(Feld[newx][newy]) ||
13524 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13525 canPassField(newx, newy, move_dir)));
13528 static void CheckGravityMovement(struct PlayerInfo *player)
13530 #if USE_PLAYER_GRAVITY
13531 if (player->gravity && !player->programmed_action)
13533 if (game.gravity && !player->programmed_action)
13536 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13537 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13538 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13539 int jx = player->jx, jy = player->jy;
13540 boolean player_is_moving_to_valid_field =
13541 (!player_is_snapping &&
13542 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13543 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13544 boolean player_can_fall_down = canFallDown(player);
13546 if (player_can_fall_down &&
13547 !player_is_moving_to_valid_field)
13548 player->programmed_action = MV_DOWN;
13552 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13554 return CheckGravityMovement(player);
13556 #if USE_PLAYER_GRAVITY
13557 if (player->gravity && !player->programmed_action)
13559 if (game.gravity && !player->programmed_action)
13562 int jx = player->jx, jy = player->jy;
13563 boolean field_under_player_is_free =
13564 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13565 boolean player_is_standing_on_valid_field =
13566 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13567 (IS_WALKABLE(Feld[jx][jy]) &&
13568 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13570 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13571 player->programmed_action = MV_DOWN;
13576 MovePlayerOneStep()
13577 -----------------------------------------------------------------------------
13578 dx, dy: direction (non-diagonal) to try to move the player to
13579 real_dx, real_dy: direction as read from input device (can be diagonal)
13582 boolean MovePlayerOneStep(struct PlayerInfo *player,
13583 int dx, int dy, int real_dx, int real_dy)
13585 int jx = player->jx, jy = player->jy;
13586 int new_jx = jx + dx, new_jy = jy + dy;
13587 #if !USE_FIXED_DONT_RUN_INTO
13591 boolean player_can_move = !player->cannot_move;
13593 if (!player->active || (!dx && !dy))
13594 return MP_NO_ACTION;
13596 player->MovDir = (dx < 0 ? MV_LEFT :
13597 dx > 0 ? MV_RIGHT :
13599 dy > 0 ? MV_DOWN : MV_NONE);
13601 if (!IN_LEV_FIELD(new_jx, new_jy))
13602 return MP_NO_ACTION;
13604 if (!player_can_move)
13606 if (player->MovPos == 0)
13608 player->is_moving = FALSE;
13609 player->is_digging = FALSE;
13610 player->is_collecting = FALSE;
13611 player->is_snapping = FALSE;
13612 player->is_pushing = FALSE;
13617 if (!options.network && game.centered_player_nr == -1 &&
13618 !AllPlayersInSight(player, new_jx, new_jy))
13619 return MP_NO_ACTION;
13621 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13622 return MP_NO_ACTION;
13625 #if !USE_FIXED_DONT_RUN_INTO
13626 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13628 /* (moved to DigField()) */
13629 if (player_can_move && DONT_RUN_INTO(element))
13631 if (element == EL_ACID && dx == 0 && dy == 1)
13633 SplashAcid(new_jx, new_jy);
13634 Feld[jx][jy] = EL_PLAYER_1;
13635 InitMovingField(jx, jy, MV_DOWN);
13636 Store[jx][jy] = EL_ACID;
13637 ContinueMoving(jx, jy);
13638 BuryPlayer(player);
13641 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13647 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13648 if (can_move != MP_MOVING)
13651 /* check if DigField() has caused relocation of the player */
13652 if (player->jx != jx || player->jy != jy)
13653 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13655 StorePlayer[jx][jy] = 0;
13656 player->last_jx = jx;
13657 player->last_jy = jy;
13658 player->jx = new_jx;
13659 player->jy = new_jy;
13660 StorePlayer[new_jx][new_jy] = player->element_nr;
13662 if (player->move_delay_value_next != -1)
13664 player->move_delay_value = player->move_delay_value_next;
13665 player->move_delay_value_next = -1;
13669 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13671 player->step_counter++;
13673 PlayerVisit[jx][jy] = FrameCounter;
13675 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13676 player->is_moving = TRUE;
13680 /* should better be called in MovePlayer(), but this breaks some tapes */
13681 ScrollPlayer(player, SCROLL_INIT);
13687 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13689 int jx = player->jx, jy = player->jy;
13690 int old_jx = jx, old_jy = jy;
13691 int moved = MP_NO_ACTION;
13693 if (!player->active)
13698 if (player->MovPos == 0)
13700 player->is_moving = FALSE;
13701 player->is_digging = FALSE;
13702 player->is_collecting = FALSE;
13703 player->is_snapping = FALSE;
13704 player->is_pushing = FALSE;
13710 if (player->move_delay > 0)
13713 player->move_delay = -1; /* set to "uninitialized" value */
13715 /* store if player is automatically moved to next field */
13716 player->is_auto_moving = (player->programmed_action != MV_NONE);
13718 /* remove the last programmed player action */
13719 player->programmed_action = 0;
13721 if (player->MovPos)
13723 /* should only happen if pre-1.2 tape recordings are played */
13724 /* this is only for backward compatibility */
13726 int original_move_delay_value = player->move_delay_value;
13729 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13733 /* scroll remaining steps with finest movement resolution */
13734 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13736 while (player->MovPos)
13738 ScrollPlayer(player, SCROLL_GO_ON);
13739 ScrollScreen(NULL, SCROLL_GO_ON);
13741 AdvanceFrameAndPlayerCounters(player->index_nr);
13747 player->move_delay_value = original_move_delay_value;
13750 player->is_active = FALSE;
13752 if (player->last_move_dir & MV_HORIZONTAL)
13754 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13755 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13759 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13760 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13763 #if USE_FIXED_BORDER_RUNNING_GFX
13764 if (!moved && !player->is_active)
13766 player->is_moving = FALSE;
13767 player->is_digging = FALSE;
13768 player->is_collecting = FALSE;
13769 player->is_snapping = FALSE;
13770 player->is_pushing = FALSE;
13778 if (moved & MP_MOVING && !ScreenMovPos &&
13779 (player->index_nr == game.centered_player_nr ||
13780 game.centered_player_nr == -1))
13782 if (moved & MP_MOVING && !ScreenMovPos &&
13783 (player == local_player || !options.network))
13786 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13787 int offset = game.scroll_delay_value;
13789 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13791 /* actual player has left the screen -- scroll in that direction */
13792 if (jx != old_jx) /* player has moved horizontally */
13793 scroll_x += (jx - old_jx);
13794 else /* player has moved vertically */
13795 scroll_y += (jy - old_jy);
13799 if (jx != old_jx) /* player has moved horizontally */
13801 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13802 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13803 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13805 /* don't scroll over playfield boundaries */
13806 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13807 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13809 /* don't scroll more than one field at a time */
13810 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13812 /* don't scroll against the player's moving direction */
13813 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13814 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13815 scroll_x = old_scroll_x;
13817 else /* player has moved vertically */
13819 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13820 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13821 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13823 /* don't scroll over playfield boundaries */
13824 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13825 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13827 /* don't scroll more than one field at a time */
13828 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13830 /* don't scroll against the player's moving direction */
13831 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13832 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13833 scroll_y = old_scroll_y;
13837 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13840 if (!options.network && game.centered_player_nr == -1 &&
13841 !AllPlayersInVisibleScreen())
13843 scroll_x = old_scroll_x;
13844 scroll_y = old_scroll_y;
13848 if (!options.network && !AllPlayersInVisibleScreen())
13850 scroll_x = old_scroll_x;
13851 scroll_y = old_scroll_y;
13856 ScrollScreen(player, SCROLL_INIT);
13857 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13862 player->StepFrame = 0;
13864 if (moved & MP_MOVING)
13866 if (old_jx != jx && old_jy == jy)
13867 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13868 else if (old_jx == jx && old_jy != jy)
13869 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13871 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13873 player->last_move_dir = player->MovDir;
13874 player->is_moving = TRUE;
13875 player->is_snapping = FALSE;
13876 player->is_switching = FALSE;
13877 player->is_dropping = FALSE;
13878 player->is_dropping_pressed = FALSE;
13879 player->drop_pressed_delay = 0;
13882 /* should better be called here than above, but this breaks some tapes */
13883 ScrollPlayer(player, SCROLL_INIT);
13888 CheckGravityMovementWhenNotMoving(player);
13890 player->is_moving = FALSE;
13892 /* at this point, the player is allowed to move, but cannot move right now
13893 (e.g. because of something blocking the way) -- ensure that the player
13894 is also allowed to move in the next frame (in old versions before 3.1.1,
13895 the player was forced to wait again for eight frames before next try) */
13897 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13898 player->move_delay = 0; /* allow direct movement in the next frame */
13901 if (player->move_delay == -1) /* not yet initialized by DigField() */
13902 player->move_delay = player->move_delay_value;
13904 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13906 TestIfPlayerTouchesBadThing(jx, jy);
13907 TestIfPlayerTouchesCustomElement(jx, jy);
13910 if (!player->active)
13911 RemovePlayer(player);
13916 void ScrollPlayer(struct PlayerInfo *player, int mode)
13918 int jx = player->jx, jy = player->jy;
13919 int last_jx = player->last_jx, last_jy = player->last_jy;
13920 int move_stepsize = TILEX / player->move_delay_value;
13922 #if USE_NEW_PLAYER_SPEED
13923 if (!player->active)
13926 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13929 if (!player->active || player->MovPos == 0)
13933 if (mode == SCROLL_INIT)
13935 player->actual_frame_counter = FrameCounter;
13936 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13938 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13939 Feld[last_jx][last_jy] == EL_EMPTY)
13941 int last_field_block_delay = 0; /* start with no blocking at all */
13942 int block_delay_adjustment = player->block_delay_adjustment;
13944 /* if player blocks last field, add delay for exactly one move */
13945 if (player->block_last_field)
13947 last_field_block_delay += player->move_delay_value;
13949 /* when blocking enabled, prevent moving up despite gravity */
13950 #if USE_PLAYER_GRAVITY
13951 if (player->gravity && player->MovDir == MV_UP)
13952 block_delay_adjustment = -1;
13954 if (game.gravity && player->MovDir == MV_UP)
13955 block_delay_adjustment = -1;
13959 /* add block delay adjustment (also possible when not blocking) */
13960 last_field_block_delay += block_delay_adjustment;
13962 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13963 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13966 #if USE_NEW_PLAYER_SPEED
13967 if (player->MovPos != 0) /* player has not yet reached destination */
13973 else if (!FrameReached(&player->actual_frame_counter, 1))
13976 #if USE_NEW_PLAYER_SPEED
13977 if (player->MovPos != 0)
13979 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13980 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13982 /* before DrawPlayer() to draw correct player graphic for this case */
13983 if (player->MovPos == 0)
13984 CheckGravityMovement(player);
13987 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13988 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13990 /* before DrawPlayer() to draw correct player graphic for this case */
13991 if (player->MovPos == 0)
13992 CheckGravityMovement(player);
13995 if (player->MovPos == 0) /* player reached destination field */
13997 if (player->move_delay_reset_counter > 0)
13999 player->move_delay_reset_counter--;
14001 if (player->move_delay_reset_counter == 0)
14003 /* continue with normal speed after quickly moving through gate */
14004 HALVE_PLAYER_SPEED(player);
14006 /* be able to make the next move without delay */
14007 player->move_delay = 0;
14011 player->last_jx = jx;
14012 player->last_jy = jy;
14014 if (Feld[jx][jy] == EL_EXIT_OPEN ||
14015 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
14017 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
14019 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
14020 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
14022 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
14024 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
14025 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
14027 DrawPlayer(player); /* needed here only to cleanup last field */
14028 RemovePlayer(player);
14030 if (local_player->friends_still_needed == 0 ||
14031 IS_SP_ELEMENT(Feld[jx][jy]))
14032 PlayerWins(player);
14035 /* this breaks one level: "machine", level 000 */
14037 int move_direction = player->MovDir;
14038 int enter_side = MV_DIR_OPPOSITE(move_direction);
14039 int leave_side = move_direction;
14040 int old_jx = last_jx;
14041 int old_jy = last_jy;
14042 int old_element = Feld[old_jx][old_jy];
14043 int new_element = Feld[jx][jy];
14045 if (IS_CUSTOM_ELEMENT(old_element))
14046 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14048 player->index_bit, leave_side);
14050 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14051 CE_PLAYER_LEAVES_X,
14052 player->index_bit, leave_side);
14054 if (IS_CUSTOM_ELEMENT(new_element))
14055 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14056 player->index_bit, enter_side);
14058 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14059 CE_PLAYER_ENTERS_X,
14060 player->index_bit, enter_side);
14062 #if USE_FIX_CE_ACTION_WITH_PLAYER
14063 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14064 CE_MOVE_OF_X, move_direction);
14066 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14067 CE_MOVE_OF_X, move_direction);
14071 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14073 TestIfPlayerTouchesBadThing(jx, jy);
14074 TestIfPlayerTouchesCustomElement(jx, jy);
14076 /* needed because pushed element has not yet reached its destination,
14077 so it would trigger a change event at its previous field location */
14078 if (!player->is_pushing)
14079 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14081 if (!player->active)
14082 RemovePlayer(player);
14085 if (!local_player->LevelSolved && level.use_step_counter)
14095 if (TimeLeft <= 10 && setup.time_limit)
14096 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14099 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14101 DisplayGameControlValues();
14103 DrawGameValue_Time(TimeLeft);
14106 if (!TimeLeft && setup.time_limit)
14107 for (i = 0; i < MAX_PLAYERS; i++)
14108 KillPlayer(&stored_player[i]);
14111 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14113 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14115 DisplayGameControlValues();
14118 else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
14119 DrawGameValue_Time(TimePlayed);
14123 if (tape.single_step && tape.recording && !tape.pausing &&
14124 !player->programmed_action)
14125 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14129 void ScrollScreen(struct PlayerInfo *player, int mode)
14131 static unsigned int screen_frame_counter = 0;
14133 if (mode == SCROLL_INIT)
14135 /* set scrolling step size according to actual player's moving speed */
14136 ScrollStepSize = TILEX / player->move_delay_value;
14138 screen_frame_counter = FrameCounter;
14139 ScreenMovDir = player->MovDir;
14140 ScreenMovPos = player->MovPos;
14141 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14144 else if (!FrameReached(&screen_frame_counter, 1))
14149 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14150 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14151 redraw_mask |= REDRAW_FIELD;
14154 ScreenMovDir = MV_NONE;
14157 void TestIfPlayerTouchesCustomElement(int x, int y)
14159 static int xy[4][2] =
14166 static int trigger_sides[4][2] =
14168 /* center side border side */
14169 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14170 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14171 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14172 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14174 static int touch_dir[4] =
14176 MV_LEFT | MV_RIGHT,
14181 int center_element = Feld[x][y]; /* should always be non-moving! */
14184 for (i = 0; i < NUM_DIRECTIONS; i++)
14186 int xx = x + xy[i][0];
14187 int yy = y + xy[i][1];
14188 int center_side = trigger_sides[i][0];
14189 int border_side = trigger_sides[i][1];
14190 int border_element;
14192 if (!IN_LEV_FIELD(xx, yy))
14195 if (IS_PLAYER(x, y)) /* player found at center element */
14197 struct PlayerInfo *player = PLAYERINFO(x, y);
14199 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14200 border_element = Feld[xx][yy]; /* may be moving! */
14201 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14202 border_element = Feld[xx][yy];
14203 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14204 border_element = MovingOrBlocked2Element(xx, yy);
14206 continue; /* center and border element do not touch */
14208 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14209 player->index_bit, border_side);
14210 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14211 CE_PLAYER_TOUCHES_X,
14212 player->index_bit, border_side);
14214 #if USE_FIX_CE_ACTION_WITH_PLAYER
14216 /* use player element that is initially defined in the level playfield,
14217 not the player element that corresponds to the runtime player number
14218 (example: a level that contains EL_PLAYER_3 as the only player would
14219 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14220 int player_element = PLAYERINFO(x, y)->initial_element;
14222 CheckElementChangeBySide(xx, yy, border_element, player_element,
14223 CE_TOUCHING_X, border_side);
14227 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14229 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14231 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14233 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14234 continue; /* center and border element do not touch */
14237 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14238 player->index_bit, center_side);
14239 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14240 CE_PLAYER_TOUCHES_X,
14241 player->index_bit, center_side);
14243 #if USE_FIX_CE_ACTION_WITH_PLAYER
14245 /* use player element that is initially defined in the level playfield,
14246 not the player element that corresponds to the runtime player number
14247 (example: a level that contains EL_PLAYER_3 as the only player would
14248 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14249 int player_element = PLAYERINFO(xx, yy)->initial_element;
14251 CheckElementChangeBySide(x, y, center_element, player_element,
14252 CE_TOUCHING_X, center_side);
14261 #if USE_ELEMENT_TOUCHING_BUGFIX
14263 void TestIfElementTouchesCustomElement(int x, int y)
14265 static int xy[4][2] =
14272 static int trigger_sides[4][2] =
14274 /* center side border side */
14275 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14276 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14277 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14278 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14280 static int touch_dir[4] =
14282 MV_LEFT | MV_RIGHT,
14287 boolean change_center_element = FALSE;
14288 int center_element = Feld[x][y]; /* should always be non-moving! */
14289 int border_element_old[NUM_DIRECTIONS];
14292 for (i = 0; i < NUM_DIRECTIONS; i++)
14294 int xx = x + xy[i][0];
14295 int yy = y + xy[i][1];
14296 int border_element;
14298 border_element_old[i] = -1;
14300 if (!IN_LEV_FIELD(xx, yy))
14303 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14304 border_element = Feld[xx][yy]; /* may be moving! */
14305 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14306 border_element = Feld[xx][yy];
14307 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14308 border_element = MovingOrBlocked2Element(xx, yy);
14310 continue; /* center and border element do not touch */
14312 border_element_old[i] = border_element;
14315 for (i = 0; i < NUM_DIRECTIONS; i++)
14317 int xx = x + xy[i][0];
14318 int yy = y + xy[i][1];
14319 int center_side = trigger_sides[i][0];
14320 int border_element = border_element_old[i];
14322 if (border_element == -1)
14325 /* check for change of border element */
14326 CheckElementChangeBySide(xx, yy, border_element, center_element,
14327 CE_TOUCHING_X, center_side);
14329 /* (center element cannot be player, so we dont have to check this here) */
14332 for (i = 0; i < NUM_DIRECTIONS; i++)
14334 int xx = x + xy[i][0];
14335 int yy = y + xy[i][1];
14336 int border_side = trigger_sides[i][1];
14337 int border_element = border_element_old[i];
14339 if (border_element == -1)
14342 /* check for change of center element (but change it only once) */
14343 if (!change_center_element)
14344 change_center_element =
14345 CheckElementChangeBySide(x, y, center_element, border_element,
14346 CE_TOUCHING_X, border_side);
14348 #if USE_FIX_CE_ACTION_WITH_PLAYER
14349 if (IS_PLAYER(xx, yy))
14351 /* use player element that is initially defined in the level playfield,
14352 not the player element that corresponds to the runtime player number
14353 (example: a level that contains EL_PLAYER_3 as the only player would
14354 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14355 int player_element = PLAYERINFO(xx, yy)->initial_element;
14357 CheckElementChangeBySide(x, y, center_element, player_element,
14358 CE_TOUCHING_X, border_side);
14366 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14368 static int xy[4][2] =
14375 static int trigger_sides[4][2] =
14377 /* center side border side */
14378 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14379 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14380 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14381 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14383 static int touch_dir[4] =
14385 MV_LEFT | MV_RIGHT,
14390 boolean change_center_element = FALSE;
14391 int center_element = Feld[x][y]; /* should always be non-moving! */
14394 for (i = 0; i < NUM_DIRECTIONS; i++)
14396 int xx = x + xy[i][0];
14397 int yy = y + xy[i][1];
14398 int center_side = trigger_sides[i][0];
14399 int border_side = trigger_sides[i][1];
14400 int border_element;
14402 if (!IN_LEV_FIELD(xx, yy))
14405 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14406 border_element = Feld[xx][yy]; /* may be moving! */
14407 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14408 border_element = Feld[xx][yy];
14409 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14410 border_element = MovingOrBlocked2Element(xx, yy);
14412 continue; /* center and border element do not touch */
14414 /* check for change of center element (but change it only once) */
14415 if (!change_center_element)
14416 change_center_element =
14417 CheckElementChangeBySide(x, y, center_element, border_element,
14418 CE_TOUCHING_X, border_side);
14420 /* check for change of border element */
14421 CheckElementChangeBySide(xx, yy, border_element, center_element,
14422 CE_TOUCHING_X, center_side);
14428 void TestIfElementHitsCustomElement(int x, int y, int direction)
14430 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14431 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14432 int hitx = x + dx, hity = y + dy;
14433 int hitting_element = Feld[x][y];
14434 int touched_element;
14436 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14439 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14440 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14442 if (IN_LEV_FIELD(hitx, hity))
14444 int opposite_direction = MV_DIR_OPPOSITE(direction);
14445 int hitting_side = direction;
14446 int touched_side = opposite_direction;
14447 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14448 MovDir[hitx][hity] != direction ||
14449 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14455 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14456 CE_HITTING_X, touched_side);
14458 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14459 CE_HIT_BY_X, hitting_side);
14461 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14462 CE_HIT_BY_SOMETHING, opposite_direction);
14464 #if USE_FIX_CE_ACTION_WITH_PLAYER
14465 if (IS_PLAYER(hitx, hity))
14467 /* use player element that is initially defined in the level playfield,
14468 not the player element that corresponds to the runtime player number
14469 (example: a level that contains EL_PLAYER_3 as the only player would
14470 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14471 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14473 CheckElementChangeBySide(x, y, hitting_element, player_element,
14474 CE_HITTING_X, touched_side);
14480 /* "hitting something" is also true when hitting the playfield border */
14481 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14482 CE_HITTING_SOMETHING, direction);
14486 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14488 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14489 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14490 int hitx = x + dx, hity = y + dy;
14491 int hitting_element = Feld[x][y];
14492 int touched_element;
14494 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14495 !IS_FREE(hitx, hity) &&
14496 (!IS_MOVING(hitx, hity) ||
14497 MovDir[hitx][hity] != direction ||
14498 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14501 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14505 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14509 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14510 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14512 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14513 EP_CAN_SMASH_EVERYTHING, direction);
14515 if (IN_LEV_FIELD(hitx, hity))
14517 int opposite_direction = MV_DIR_OPPOSITE(direction);
14518 int hitting_side = direction;
14519 int touched_side = opposite_direction;
14521 int touched_element = MovingOrBlocked2Element(hitx, hity);
14524 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14525 MovDir[hitx][hity] != direction ||
14526 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14535 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14536 CE_SMASHED_BY_SOMETHING, opposite_direction);
14538 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14539 CE_OTHER_IS_SMASHING, touched_side);
14541 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14542 CE_OTHER_GETS_SMASHED, hitting_side);
14548 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14550 int i, kill_x = -1, kill_y = -1;
14552 int bad_element = -1;
14553 static int test_xy[4][2] =
14560 static int test_dir[4] =
14568 for (i = 0; i < NUM_DIRECTIONS; i++)
14570 int test_x, test_y, test_move_dir, test_element;
14572 test_x = good_x + test_xy[i][0];
14573 test_y = good_y + test_xy[i][1];
14575 if (!IN_LEV_FIELD(test_x, test_y))
14579 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14581 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14583 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14584 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14586 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14587 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14591 bad_element = test_element;
14597 if (kill_x != -1 || kill_y != -1)
14599 if (IS_PLAYER(good_x, good_y))
14601 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14603 if (player->shield_deadly_time_left > 0 &&
14604 !IS_INDESTRUCTIBLE(bad_element))
14605 Bang(kill_x, kill_y);
14606 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14607 KillPlayer(player);
14610 Bang(good_x, good_y);
14614 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14616 int i, kill_x = -1, kill_y = -1;
14617 int bad_element = Feld[bad_x][bad_y];
14618 static int test_xy[4][2] =
14625 static int touch_dir[4] =
14627 MV_LEFT | MV_RIGHT,
14632 static int test_dir[4] =
14640 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14643 for (i = 0; i < NUM_DIRECTIONS; i++)
14645 int test_x, test_y, test_move_dir, test_element;
14647 test_x = bad_x + test_xy[i][0];
14648 test_y = bad_y + test_xy[i][1];
14650 if (!IN_LEV_FIELD(test_x, test_y))
14654 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14656 test_element = Feld[test_x][test_y];
14658 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14659 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14661 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14662 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14664 /* good thing is player or penguin that does not move away */
14665 if (IS_PLAYER(test_x, test_y))
14667 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14669 if (bad_element == EL_ROBOT && player->is_moving)
14670 continue; /* robot does not kill player if he is moving */
14672 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14674 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14675 continue; /* center and border element do not touch */
14683 else if (test_element == EL_PENGUIN)
14693 if (kill_x != -1 || kill_y != -1)
14695 if (IS_PLAYER(kill_x, kill_y))
14697 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14699 if (player->shield_deadly_time_left > 0 &&
14700 !IS_INDESTRUCTIBLE(bad_element))
14701 Bang(bad_x, bad_y);
14702 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14703 KillPlayer(player);
14706 Bang(kill_x, kill_y);
14710 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14712 int bad_element = Feld[bad_x][bad_y];
14713 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14714 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14715 int test_x = bad_x + dx, test_y = bad_y + dy;
14716 int test_move_dir, test_element;
14717 int kill_x = -1, kill_y = -1;
14719 if (!IN_LEV_FIELD(test_x, test_y))
14723 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14725 test_element = Feld[test_x][test_y];
14727 if (test_move_dir != bad_move_dir)
14729 /* good thing can be player or penguin that does not move away */
14730 if (IS_PLAYER(test_x, test_y))
14732 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14734 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14735 player as being hit when he is moving towards the bad thing, because
14736 the "get hit by" condition would be lost after the player stops) */
14737 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14738 return; /* player moves away from bad thing */
14743 else if (test_element == EL_PENGUIN)
14750 if (kill_x != -1 || kill_y != -1)
14752 if (IS_PLAYER(kill_x, kill_y))
14754 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14756 if (player->shield_deadly_time_left > 0 &&
14757 !IS_INDESTRUCTIBLE(bad_element))
14758 Bang(bad_x, bad_y);
14759 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14760 KillPlayer(player);
14763 Bang(kill_x, kill_y);
14767 void TestIfPlayerTouchesBadThing(int x, int y)
14769 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14772 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14774 TestIfGoodThingHitsBadThing(x, y, move_dir);
14777 void TestIfBadThingTouchesPlayer(int x, int y)
14779 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14782 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14784 TestIfBadThingHitsGoodThing(x, y, move_dir);
14787 void TestIfFriendTouchesBadThing(int x, int y)
14789 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14792 void TestIfBadThingTouchesFriend(int x, int y)
14794 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14797 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14799 int i, kill_x = bad_x, kill_y = bad_y;
14800 static int xy[4][2] =
14808 for (i = 0; i < NUM_DIRECTIONS; i++)
14812 x = bad_x + xy[i][0];
14813 y = bad_y + xy[i][1];
14814 if (!IN_LEV_FIELD(x, y))
14817 element = Feld[x][y];
14818 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14819 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14827 if (kill_x != bad_x || kill_y != bad_y)
14828 Bang(bad_x, bad_y);
14831 void KillPlayer(struct PlayerInfo *player)
14833 int jx = player->jx, jy = player->jy;
14835 if (!player->active)
14839 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14840 player->killed, player->active, player->reanimated);
14843 /* the following code was introduced to prevent an infinite loop when calling
14845 -> CheckTriggeredElementChangeExt()
14846 -> ExecuteCustomElementAction()
14848 -> (infinitely repeating the above sequence of function calls)
14849 which occurs when killing the player while having a CE with the setting
14850 "kill player X when explosion of <player X>"; the solution using a new
14851 field "player->killed" was chosen for backwards compatibility, although
14852 clever use of the fields "player->active" etc. would probably also work */
14854 if (player->killed)
14858 player->killed = TRUE;
14860 /* remove accessible field at the player's position */
14861 Feld[jx][jy] = EL_EMPTY;
14863 /* deactivate shield (else Bang()/Explode() would not work right) */
14864 player->shield_normal_time_left = 0;
14865 player->shield_deadly_time_left = 0;
14868 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14869 player->killed, player->active, player->reanimated);
14875 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14876 player->killed, player->active, player->reanimated);
14879 #if USE_PLAYER_REANIMATION
14881 if (player->reanimated) /* killed player may have been reanimated */
14882 player->killed = player->reanimated = FALSE;
14884 BuryPlayer(player);
14886 if (player->killed) /* player may have been reanimated */
14887 BuryPlayer(player);
14890 BuryPlayer(player);
14894 static void KillPlayerUnlessEnemyProtected(int x, int y)
14896 if (!PLAYER_ENEMY_PROTECTED(x, y))
14897 KillPlayer(PLAYERINFO(x, y));
14900 static void KillPlayerUnlessExplosionProtected(int x, int y)
14902 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14903 KillPlayer(PLAYERINFO(x, y));
14906 void BuryPlayer(struct PlayerInfo *player)
14908 int jx = player->jx, jy = player->jy;
14910 if (!player->active)
14913 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14914 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14916 player->GameOver = TRUE;
14917 RemovePlayer(player);
14920 void RemovePlayer(struct PlayerInfo *player)
14922 int jx = player->jx, jy = player->jy;
14923 int i, found = FALSE;
14925 player->present = FALSE;
14926 player->active = FALSE;
14928 if (!ExplodeField[jx][jy])
14929 StorePlayer[jx][jy] = 0;
14931 if (player->is_moving)
14932 TEST_DrawLevelField(player->last_jx, player->last_jy);
14934 for (i = 0; i < MAX_PLAYERS; i++)
14935 if (stored_player[i].active)
14939 AllPlayersGone = TRUE;
14945 #if USE_NEW_SNAP_DELAY
14946 static void setFieldForSnapping(int x, int y, int element, int direction)
14948 struct ElementInfo *ei = &element_info[element];
14949 int direction_bit = MV_DIR_TO_BIT(direction);
14950 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14951 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14952 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14954 Feld[x][y] = EL_ELEMENT_SNAPPING;
14955 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14957 ResetGfxAnimation(x, y);
14959 GfxElement[x][y] = element;
14960 GfxAction[x][y] = action;
14961 GfxDir[x][y] = direction;
14962 GfxFrame[x][y] = -1;
14967 =============================================================================
14968 checkDiagonalPushing()
14969 -----------------------------------------------------------------------------
14970 check if diagonal input device direction results in pushing of object
14971 (by checking if the alternative direction is walkable, diggable, ...)
14972 =============================================================================
14975 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14976 int x, int y, int real_dx, int real_dy)
14978 int jx, jy, dx, dy, xx, yy;
14980 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14983 /* diagonal direction: check alternative direction */
14988 xx = jx + (dx == 0 ? real_dx : 0);
14989 yy = jy + (dy == 0 ? real_dy : 0);
14991 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14995 =============================================================================
14997 -----------------------------------------------------------------------------
14998 x, y: field next to player (non-diagonal) to try to dig to
14999 real_dx, real_dy: direction as read from input device (can be diagonal)
15000 =============================================================================
15003 static int DigField(struct PlayerInfo *player,
15004 int oldx, int oldy, int x, int y,
15005 int real_dx, int real_dy, int mode)
15007 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
15008 boolean player_was_pushing = player->is_pushing;
15009 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
15010 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
15011 int jx = oldx, jy = oldy;
15012 int dx = x - jx, dy = y - jy;
15013 int nextx = x + dx, nexty = y + dy;
15014 int move_direction = (dx == -1 ? MV_LEFT :
15015 dx == +1 ? MV_RIGHT :
15017 dy == +1 ? MV_DOWN : MV_NONE);
15018 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
15019 int dig_side = MV_DIR_OPPOSITE(move_direction);
15020 int old_element = Feld[jx][jy];
15021 #if USE_FIXED_DONT_RUN_INTO
15022 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
15028 if (is_player) /* function can also be called by EL_PENGUIN */
15030 if (player->MovPos == 0)
15032 player->is_digging = FALSE;
15033 player->is_collecting = FALSE;
15036 if (player->MovPos == 0) /* last pushing move finished */
15037 player->is_pushing = FALSE;
15039 if (mode == DF_NO_PUSH) /* player just stopped pushing */
15041 player->is_switching = FALSE;
15042 player->push_delay = -1;
15044 return MP_NO_ACTION;
15048 #if !USE_FIXED_DONT_RUN_INTO
15049 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15050 return MP_NO_ACTION;
15053 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15054 old_element = Back[jx][jy];
15056 /* in case of element dropped at player position, check background */
15057 else if (Back[jx][jy] != EL_EMPTY &&
15058 game.engine_version >= VERSION_IDENT(2,2,0,0))
15059 old_element = Back[jx][jy];
15061 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15062 return MP_NO_ACTION; /* field has no opening in this direction */
15064 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15065 return MP_NO_ACTION; /* field has no opening in this direction */
15067 #if USE_FIXED_DONT_RUN_INTO
15068 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15072 Feld[jx][jy] = player->artwork_element;
15073 InitMovingField(jx, jy, MV_DOWN);
15074 Store[jx][jy] = EL_ACID;
15075 ContinueMoving(jx, jy);
15076 BuryPlayer(player);
15078 return MP_DONT_RUN_INTO;
15082 #if USE_FIXED_DONT_RUN_INTO
15083 if (player_can_move && DONT_RUN_INTO(element))
15085 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15087 return MP_DONT_RUN_INTO;
15091 #if USE_FIXED_DONT_RUN_INTO
15092 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15093 return MP_NO_ACTION;
15096 #if !USE_FIXED_DONT_RUN_INTO
15097 element = Feld[x][y];
15100 collect_count = element_info[element].collect_count_initial;
15102 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15103 return MP_NO_ACTION;
15105 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15106 player_can_move = player_can_move_or_snap;
15108 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15109 game.engine_version >= VERSION_IDENT(2,2,0,0))
15111 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15112 player->index_bit, dig_side);
15113 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15114 player->index_bit, dig_side);
15116 if (element == EL_DC_LANDMINE)
15119 if (Feld[x][y] != element) /* field changed by snapping */
15122 return MP_NO_ACTION;
15125 #if USE_PLAYER_GRAVITY
15126 if (player->gravity && is_player && !player->is_auto_moving &&
15127 canFallDown(player) && move_direction != MV_DOWN &&
15128 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15129 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15131 if (game.gravity && is_player && !player->is_auto_moving &&
15132 canFallDown(player) && move_direction != MV_DOWN &&
15133 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15134 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15137 if (player_can_move &&
15138 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15140 int sound_element = SND_ELEMENT(element);
15141 int sound_action = ACTION_WALKING;
15143 if (IS_RND_GATE(element))
15145 if (!player->key[RND_GATE_NR(element)])
15146 return MP_NO_ACTION;
15148 else if (IS_RND_GATE_GRAY(element))
15150 if (!player->key[RND_GATE_GRAY_NR(element)])
15151 return MP_NO_ACTION;
15153 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15155 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15156 return MP_NO_ACTION;
15158 else if (element == EL_EXIT_OPEN ||
15159 element == EL_EM_EXIT_OPEN ||
15161 element == EL_EM_EXIT_OPENING ||
15163 element == EL_STEEL_EXIT_OPEN ||
15164 element == EL_EM_STEEL_EXIT_OPEN ||
15166 element == EL_EM_STEEL_EXIT_OPENING ||
15168 element == EL_SP_EXIT_OPEN ||
15169 element == EL_SP_EXIT_OPENING)
15171 sound_action = ACTION_PASSING; /* player is passing exit */
15173 else if (element == EL_EMPTY)
15175 sound_action = ACTION_MOVING; /* nothing to walk on */
15178 /* play sound from background or player, whatever is available */
15179 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15180 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15182 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15184 else if (player_can_move &&
15185 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15187 if (!ACCESS_FROM(element, opposite_direction))
15188 return MP_NO_ACTION; /* field not accessible from this direction */
15190 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15191 return MP_NO_ACTION;
15193 if (IS_EM_GATE(element))
15195 if (!player->key[EM_GATE_NR(element)])
15196 return MP_NO_ACTION;
15198 else if (IS_EM_GATE_GRAY(element))
15200 if (!player->key[EM_GATE_GRAY_NR(element)])
15201 return MP_NO_ACTION;
15203 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15205 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15206 return MP_NO_ACTION;
15208 else if (IS_EMC_GATE(element))
15210 if (!player->key[EMC_GATE_NR(element)])
15211 return MP_NO_ACTION;
15213 else if (IS_EMC_GATE_GRAY(element))
15215 if (!player->key[EMC_GATE_GRAY_NR(element)])
15216 return MP_NO_ACTION;
15218 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15220 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15221 return MP_NO_ACTION;
15223 else if (element == EL_DC_GATE_WHITE ||
15224 element == EL_DC_GATE_WHITE_GRAY ||
15225 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15227 if (player->num_white_keys == 0)
15228 return MP_NO_ACTION;
15230 player->num_white_keys--;
15232 else if (IS_SP_PORT(element))
15234 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15235 element == EL_SP_GRAVITY_PORT_RIGHT ||
15236 element == EL_SP_GRAVITY_PORT_UP ||
15237 element == EL_SP_GRAVITY_PORT_DOWN)
15238 #if USE_PLAYER_GRAVITY
15239 player->gravity = !player->gravity;
15241 game.gravity = !game.gravity;
15243 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15244 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15245 element == EL_SP_GRAVITY_ON_PORT_UP ||
15246 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15247 #if USE_PLAYER_GRAVITY
15248 player->gravity = TRUE;
15250 game.gravity = TRUE;
15252 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15253 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15254 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15255 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15256 #if USE_PLAYER_GRAVITY
15257 player->gravity = FALSE;
15259 game.gravity = FALSE;
15263 /* automatically move to the next field with double speed */
15264 player->programmed_action = move_direction;
15266 if (player->move_delay_reset_counter == 0)
15268 player->move_delay_reset_counter = 2; /* two double speed steps */
15270 DOUBLE_PLAYER_SPEED(player);
15273 PlayLevelSoundAction(x, y, ACTION_PASSING);
15275 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15279 if (mode != DF_SNAP)
15281 GfxElement[x][y] = GFX_ELEMENT(element);
15282 player->is_digging = TRUE;
15285 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15287 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15288 player->index_bit, dig_side);
15290 if (mode == DF_SNAP)
15292 #if USE_NEW_SNAP_DELAY
15293 if (level.block_snap_field)
15294 setFieldForSnapping(x, y, element, move_direction);
15296 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15298 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15301 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15302 player->index_bit, dig_side);
15305 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15309 if (is_player && mode != DF_SNAP)
15311 GfxElement[x][y] = element;
15312 player->is_collecting = TRUE;
15315 if (element == EL_SPEED_PILL)
15317 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15319 else if (element == EL_EXTRA_TIME && level.time > 0)
15321 TimeLeft += level.extra_time;
15324 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15326 DisplayGameControlValues();
15328 DrawGameValue_Time(TimeLeft);
15331 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15333 player->shield_normal_time_left += level.shield_normal_time;
15334 if (element == EL_SHIELD_DEADLY)
15335 player->shield_deadly_time_left += level.shield_deadly_time;
15337 else if (element == EL_DYNAMITE ||
15338 element == EL_EM_DYNAMITE ||
15339 element == EL_SP_DISK_RED)
15341 if (player->inventory_size < MAX_INVENTORY_SIZE)
15342 player->inventory_element[player->inventory_size++] = element;
15344 DrawGameDoorValues();
15346 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15348 player->dynabomb_count++;
15349 player->dynabombs_left++;
15351 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15353 player->dynabomb_size++;
15355 else if (element == EL_DYNABOMB_INCREASE_POWER)
15357 player->dynabomb_xl = TRUE;
15359 else if (IS_KEY(element))
15361 player->key[KEY_NR(element)] = TRUE;
15363 DrawGameDoorValues();
15365 else if (element == EL_DC_KEY_WHITE)
15367 player->num_white_keys++;
15369 /* display white keys? */
15370 /* DrawGameDoorValues(); */
15372 else if (IS_ENVELOPE(element))
15374 player->show_envelope = element;
15376 else if (element == EL_EMC_LENSES)
15378 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15380 RedrawAllInvisibleElementsForLenses();
15382 else if (element == EL_EMC_MAGNIFIER)
15384 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15386 RedrawAllInvisibleElementsForMagnifier();
15388 else if (IS_DROPPABLE(element) ||
15389 IS_THROWABLE(element)) /* can be collected and dropped */
15393 if (collect_count == 0)
15394 player->inventory_infinite_element = element;
15396 for (i = 0; i < collect_count; i++)
15397 if (player->inventory_size < MAX_INVENTORY_SIZE)
15398 player->inventory_element[player->inventory_size++] = element;
15400 DrawGameDoorValues();
15402 else if (collect_count > 0)
15404 local_player->gems_still_needed -= collect_count;
15405 if (local_player->gems_still_needed < 0)
15406 local_player->gems_still_needed = 0;
15409 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15411 DisplayGameControlValues();
15413 DrawGameValue_Emeralds(local_player->gems_still_needed);
15417 RaiseScoreElement(element);
15418 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15421 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15422 player->index_bit, dig_side);
15424 if (mode == DF_SNAP)
15426 #if USE_NEW_SNAP_DELAY
15427 if (level.block_snap_field)
15428 setFieldForSnapping(x, y, element, move_direction);
15430 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15432 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15435 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15436 player->index_bit, dig_side);
15439 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15441 if (mode == DF_SNAP && element != EL_BD_ROCK)
15442 return MP_NO_ACTION;
15444 if (CAN_FALL(element) && dy)
15445 return MP_NO_ACTION;
15447 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15448 !(element == EL_SPRING && level.use_spring_bug))
15449 return MP_NO_ACTION;
15451 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15452 ((move_direction & MV_VERTICAL &&
15453 ((element_info[element].move_pattern & MV_LEFT &&
15454 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15455 (element_info[element].move_pattern & MV_RIGHT &&
15456 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15457 (move_direction & MV_HORIZONTAL &&
15458 ((element_info[element].move_pattern & MV_UP &&
15459 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15460 (element_info[element].move_pattern & MV_DOWN &&
15461 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15462 return MP_NO_ACTION;
15464 /* do not push elements already moving away faster than player */
15465 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15466 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15467 return MP_NO_ACTION;
15469 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15471 if (player->push_delay_value == -1 || !player_was_pushing)
15472 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15474 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15476 if (player->push_delay_value == -1)
15477 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15479 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15481 if (!player->is_pushing)
15482 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15485 player->is_pushing = TRUE;
15486 player->is_active = TRUE;
15488 if (!(IN_LEV_FIELD(nextx, nexty) &&
15489 (IS_FREE(nextx, nexty) ||
15490 (IS_SB_ELEMENT(element) &&
15491 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15492 (IS_CUSTOM_ELEMENT(element) &&
15493 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15494 return MP_NO_ACTION;
15496 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15497 return MP_NO_ACTION;
15499 if (player->push_delay == -1) /* new pushing; restart delay */
15500 player->push_delay = 0;
15502 if (player->push_delay < player->push_delay_value &&
15503 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15504 element != EL_SPRING && element != EL_BALLOON)
15506 /* make sure that there is no move delay before next try to push */
15507 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15508 player->move_delay = 0;
15510 return MP_NO_ACTION;
15513 if (IS_CUSTOM_ELEMENT(element) &&
15514 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15516 if (!DigFieldByCE(nextx, nexty, element))
15517 return MP_NO_ACTION;
15520 if (IS_SB_ELEMENT(element))
15522 if (element == EL_SOKOBAN_FIELD_FULL)
15524 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15525 local_player->sokobanfields_still_needed++;
15528 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15530 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15531 local_player->sokobanfields_still_needed--;
15534 Feld[x][y] = EL_SOKOBAN_OBJECT;
15536 if (Back[x][y] == Back[nextx][nexty])
15537 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15538 else if (Back[x][y] != 0)
15539 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15542 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15546 if (local_player->sokobanfields_still_needed == 0 &&
15547 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15549 if (local_player->sokobanfields_still_needed == 0 &&
15550 game.emulation == EMU_SOKOBAN)
15553 PlayerWins(player);
15555 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15559 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15561 InitMovingField(x, y, move_direction);
15562 GfxAction[x][y] = ACTION_PUSHING;
15564 if (mode == DF_SNAP)
15565 ContinueMoving(x, y);
15567 MovPos[x][y] = (dx != 0 ? dx : dy);
15569 Pushed[x][y] = TRUE;
15570 Pushed[nextx][nexty] = TRUE;
15572 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15573 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15575 player->push_delay_value = -1; /* get new value later */
15577 /* check for element change _after_ element has been pushed */
15578 if (game.use_change_when_pushing_bug)
15580 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15581 player->index_bit, dig_side);
15582 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15583 player->index_bit, dig_side);
15586 else if (IS_SWITCHABLE(element))
15588 if (PLAYER_SWITCHING(player, x, y))
15590 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15591 player->index_bit, dig_side);
15596 player->is_switching = TRUE;
15597 player->switch_x = x;
15598 player->switch_y = y;
15600 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15602 if (element == EL_ROBOT_WHEEL)
15604 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15608 game.robot_wheel_active = TRUE;
15610 TEST_DrawLevelField(x, y);
15612 else if (element == EL_SP_TERMINAL)
15616 SCAN_PLAYFIELD(xx, yy)
15618 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15620 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15621 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15624 else if (IS_BELT_SWITCH(element))
15626 ToggleBeltSwitch(x, y);
15628 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15629 element == EL_SWITCHGATE_SWITCH_DOWN ||
15630 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15631 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15633 ToggleSwitchgateSwitch(x, y);
15635 else if (element == EL_LIGHT_SWITCH ||
15636 element == EL_LIGHT_SWITCH_ACTIVE)
15638 ToggleLightSwitch(x, y);
15640 else if (element == EL_TIMEGATE_SWITCH ||
15641 element == EL_DC_TIMEGATE_SWITCH)
15643 ActivateTimegateSwitch(x, y);
15645 else if (element == EL_BALLOON_SWITCH_LEFT ||
15646 element == EL_BALLOON_SWITCH_RIGHT ||
15647 element == EL_BALLOON_SWITCH_UP ||
15648 element == EL_BALLOON_SWITCH_DOWN ||
15649 element == EL_BALLOON_SWITCH_NONE ||
15650 element == EL_BALLOON_SWITCH_ANY)
15652 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15653 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15654 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15655 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15656 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15659 else if (element == EL_LAMP)
15661 Feld[x][y] = EL_LAMP_ACTIVE;
15662 local_player->lights_still_needed--;
15664 ResetGfxAnimation(x, y);
15665 TEST_DrawLevelField(x, y);
15667 else if (element == EL_TIME_ORB_FULL)
15669 Feld[x][y] = EL_TIME_ORB_EMPTY;
15671 if (level.time > 0 || level.use_time_orb_bug)
15673 TimeLeft += level.time_orb_time;
15674 game.no_time_limit = FALSE;
15677 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15679 DisplayGameControlValues();
15681 DrawGameValue_Time(TimeLeft);
15685 ResetGfxAnimation(x, y);
15686 TEST_DrawLevelField(x, y);
15688 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15689 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15693 game.ball_state = !game.ball_state;
15695 SCAN_PLAYFIELD(xx, yy)
15697 int e = Feld[xx][yy];
15699 if (game.ball_state)
15701 if (e == EL_EMC_MAGIC_BALL)
15702 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15703 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15704 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15708 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15709 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15710 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15711 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15716 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15717 player->index_bit, dig_side);
15719 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15720 player->index_bit, dig_side);
15722 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15723 player->index_bit, dig_side);
15729 if (!PLAYER_SWITCHING(player, x, y))
15731 player->is_switching = TRUE;
15732 player->switch_x = x;
15733 player->switch_y = y;
15735 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15736 player->index_bit, dig_side);
15737 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15738 player->index_bit, dig_side);
15740 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15741 player->index_bit, dig_side);
15742 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15743 player->index_bit, dig_side);
15746 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15747 player->index_bit, dig_side);
15748 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15749 player->index_bit, dig_side);
15751 return MP_NO_ACTION;
15754 player->push_delay = -1;
15756 if (is_player) /* function can also be called by EL_PENGUIN */
15758 if (Feld[x][y] != element) /* really digged/collected something */
15760 player->is_collecting = !player->is_digging;
15761 player->is_active = TRUE;
15768 static boolean DigFieldByCE(int x, int y, int digging_element)
15770 int element = Feld[x][y];
15772 if (!IS_FREE(x, y))
15774 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15775 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15778 /* no element can dig solid indestructible elements */
15779 if (IS_INDESTRUCTIBLE(element) &&
15780 !IS_DIGGABLE(element) &&
15781 !IS_COLLECTIBLE(element))
15784 if (AmoebaNr[x][y] &&
15785 (element == EL_AMOEBA_FULL ||
15786 element == EL_BD_AMOEBA ||
15787 element == EL_AMOEBA_GROWING))
15789 AmoebaCnt[AmoebaNr[x][y]]--;
15790 AmoebaCnt2[AmoebaNr[x][y]]--;
15793 if (IS_MOVING(x, y))
15794 RemoveMovingField(x, y);
15798 TEST_DrawLevelField(x, y);
15801 /* if digged element was about to explode, prevent the explosion */
15802 ExplodeField[x][y] = EX_TYPE_NONE;
15804 PlayLevelSoundAction(x, y, action);
15807 Store[x][y] = EL_EMPTY;
15810 /* this makes it possible to leave the removed element again */
15811 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15812 Store[x][y] = element;
15814 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15816 int move_leave_element = element_info[digging_element].move_leave_element;
15818 /* this makes it possible to leave the removed element again */
15819 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15820 element : move_leave_element);
15827 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15829 int jx = player->jx, jy = player->jy;
15830 int x = jx + dx, y = jy + dy;
15831 int snap_direction = (dx == -1 ? MV_LEFT :
15832 dx == +1 ? MV_RIGHT :
15834 dy == +1 ? MV_DOWN : MV_NONE);
15835 boolean can_continue_snapping = (level.continuous_snapping &&
15836 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15838 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15841 if (!player->active || !IN_LEV_FIELD(x, y))
15849 if (player->MovPos == 0)
15850 player->is_pushing = FALSE;
15852 player->is_snapping = FALSE;
15854 if (player->MovPos == 0)
15856 player->is_moving = FALSE;
15857 player->is_digging = FALSE;
15858 player->is_collecting = FALSE;
15864 #if USE_NEW_CONTINUOUS_SNAPPING
15865 /* prevent snapping with already pressed snap key when not allowed */
15866 if (player->is_snapping && !can_continue_snapping)
15869 if (player->is_snapping)
15873 player->MovDir = snap_direction;
15875 if (player->MovPos == 0)
15877 player->is_moving = FALSE;
15878 player->is_digging = FALSE;
15879 player->is_collecting = FALSE;
15882 player->is_dropping = FALSE;
15883 player->is_dropping_pressed = FALSE;
15884 player->drop_pressed_delay = 0;
15886 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15889 player->is_snapping = TRUE;
15890 player->is_active = TRUE;
15892 if (player->MovPos == 0)
15894 player->is_moving = FALSE;
15895 player->is_digging = FALSE;
15896 player->is_collecting = FALSE;
15899 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15900 TEST_DrawLevelField(player->last_jx, player->last_jy);
15902 TEST_DrawLevelField(x, y);
15907 static boolean DropElement(struct PlayerInfo *player)
15909 int old_element, new_element;
15910 int dropx = player->jx, dropy = player->jy;
15911 int drop_direction = player->MovDir;
15912 int drop_side = drop_direction;
15914 int drop_element = get_next_dropped_element(player);
15916 int drop_element = (player->inventory_size > 0 ?
15917 player->inventory_element[player->inventory_size - 1] :
15918 player->inventory_infinite_element != EL_UNDEFINED ?
15919 player->inventory_infinite_element :
15920 player->dynabombs_left > 0 ?
15921 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15925 player->is_dropping_pressed = TRUE;
15927 /* do not drop an element on top of another element; when holding drop key
15928 pressed without moving, dropped element must move away before the next
15929 element can be dropped (this is especially important if the next element
15930 is dynamite, which can be placed on background for historical reasons) */
15931 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15934 if (IS_THROWABLE(drop_element))
15936 dropx += GET_DX_FROM_DIR(drop_direction);
15937 dropy += GET_DY_FROM_DIR(drop_direction);
15939 if (!IN_LEV_FIELD(dropx, dropy))
15943 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15944 new_element = drop_element; /* default: no change when dropping */
15946 /* check if player is active, not moving and ready to drop */
15947 if (!player->active || player->MovPos || player->drop_delay > 0)
15950 /* check if player has anything that can be dropped */
15951 if (new_element == EL_UNDEFINED)
15954 /* check if drop key was pressed long enough for EM style dynamite */
15955 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15958 /* check if anything can be dropped at the current position */
15959 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15962 /* collected custom elements can only be dropped on empty fields */
15963 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15966 if (old_element != EL_EMPTY)
15967 Back[dropx][dropy] = old_element; /* store old element on this field */
15969 ResetGfxAnimation(dropx, dropy);
15970 ResetRandomAnimationValue(dropx, dropy);
15972 if (player->inventory_size > 0 ||
15973 player->inventory_infinite_element != EL_UNDEFINED)
15975 if (player->inventory_size > 0)
15977 player->inventory_size--;
15979 DrawGameDoorValues();
15981 if (new_element == EL_DYNAMITE)
15982 new_element = EL_DYNAMITE_ACTIVE;
15983 else if (new_element == EL_EM_DYNAMITE)
15984 new_element = EL_EM_DYNAMITE_ACTIVE;
15985 else if (new_element == EL_SP_DISK_RED)
15986 new_element = EL_SP_DISK_RED_ACTIVE;
15989 Feld[dropx][dropy] = new_element;
15991 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15992 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15993 el2img(Feld[dropx][dropy]), 0);
15995 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15997 /* needed if previous element just changed to "empty" in the last frame */
15998 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16000 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
16001 player->index_bit, drop_side);
16002 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
16004 player->index_bit, drop_side);
16006 TestIfElementTouchesCustomElement(dropx, dropy);
16008 else /* player is dropping a dyna bomb */
16010 player->dynabombs_left--;
16012 Feld[dropx][dropy] = new_element;
16014 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
16015 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
16016 el2img(Feld[dropx][dropy]), 0);
16018 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
16021 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
16022 InitField_WithBug1(dropx, dropy, FALSE);
16024 new_element = Feld[dropx][dropy]; /* element might have changed */
16026 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
16027 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
16029 int move_direction, nextx, nexty;
16031 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
16032 MovDir[dropx][dropy] = drop_direction;
16034 move_direction = MovDir[dropx][dropy];
16035 nextx = dropx + GET_DX_FROM_DIR(move_direction);
16036 nexty = dropy + GET_DY_FROM_DIR(move_direction);
16038 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
16040 #if USE_FIX_IMPACT_COLLISION
16041 /* do not cause impact style collision by dropping elements that can fall */
16042 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16044 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
16048 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16049 player->is_dropping = TRUE;
16051 player->drop_pressed_delay = 0;
16052 player->is_dropping_pressed = FALSE;
16054 player->drop_x = dropx;
16055 player->drop_y = dropy;
16060 /* ------------------------------------------------------------------------- */
16061 /* game sound playing functions */
16062 /* ------------------------------------------------------------------------- */
16064 static int *loop_sound_frame = NULL;
16065 static int *loop_sound_volume = NULL;
16067 void InitPlayLevelSound()
16069 int num_sounds = getSoundListSize();
16071 checked_free(loop_sound_frame);
16072 checked_free(loop_sound_volume);
16074 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16075 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16078 static void PlayLevelSound(int x, int y, int nr)
16080 int sx = SCREENX(x), sy = SCREENY(y);
16081 int volume, stereo_position;
16082 int max_distance = 8;
16083 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16085 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16086 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16089 if (!IN_LEV_FIELD(x, y) ||
16090 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16091 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16094 volume = SOUND_MAX_VOLUME;
16096 if (!IN_SCR_FIELD(sx, sy))
16098 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16099 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16101 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16104 stereo_position = (SOUND_MAX_LEFT +
16105 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16106 (SCR_FIELDX + 2 * max_distance));
16108 if (IS_LOOP_SOUND(nr))
16110 /* This assures that quieter loop sounds do not overwrite louder ones,
16111 while restarting sound volume comparison with each new game frame. */
16113 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16116 loop_sound_volume[nr] = volume;
16117 loop_sound_frame[nr] = FrameCounter;
16120 PlaySoundExt(nr, volume, stereo_position, type);
16123 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16125 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16126 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16127 y < LEVELY(BY1) ? LEVELY(BY1) :
16128 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16132 static void PlayLevelSoundAction(int x, int y, int action)
16134 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16137 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16139 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16141 if (sound_effect != SND_UNDEFINED)
16142 PlayLevelSound(x, y, sound_effect);
16145 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16148 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16150 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16151 PlayLevelSound(x, y, sound_effect);
16154 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16156 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16158 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16159 PlayLevelSound(x, y, sound_effect);
16162 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16164 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16166 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16167 StopSound(sound_effect);
16170 static void PlayLevelMusic()
16172 if (levelset.music[level_nr] != MUS_UNDEFINED)
16173 PlayMusic(levelset.music[level_nr]); /* from config file */
16175 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16178 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16180 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16181 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16182 int x = xx - 1 - offset;
16183 int y = yy - 1 - offset;
16188 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16192 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16196 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16200 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16204 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16208 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16212 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16215 case SAMPLE_android_clone:
16216 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16219 case SAMPLE_android_move:
16220 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16223 case SAMPLE_spring:
16224 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16228 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16232 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16235 case SAMPLE_eater_eat:
16236 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16240 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16243 case SAMPLE_collect:
16244 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16247 case SAMPLE_diamond:
16248 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16251 case SAMPLE_squash:
16252 /* !!! CHECK THIS !!! */
16254 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16256 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16260 case SAMPLE_wonderfall:
16261 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16265 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16269 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16273 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16277 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16281 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16285 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16288 case SAMPLE_wonder:
16289 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16293 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16296 case SAMPLE_exit_open:
16297 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16300 case SAMPLE_exit_leave:
16301 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16304 case SAMPLE_dynamite:
16305 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16309 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16313 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16317 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16321 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16325 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16329 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16333 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16338 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16340 int element = map_element_SP_to_RND(element_sp);
16341 int action = map_action_SP_to_RND(action_sp);
16342 int offset = (setup.sp_show_border_elements ? 0 : 1);
16343 int x = xx - offset;
16344 int y = yy - offset;
16347 printf("::: %d -> %d\n", element_sp, action_sp);
16350 PlayLevelSoundElementAction(x, y, element, action);
16354 void ChangeTime(int value)
16356 int *time = (game.no_time_limit ? &TimePlayed : &TimeLeft);
16360 /* EMC game engine uses value from time counter of RND game engine */
16361 level.native_em_level->lev->time = *time;
16363 DrawGameValue_Time(*time);
16366 void RaiseScore(int value)
16368 /* EMC game engine and RND game engine have separate score counters */
16369 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16370 &level.native_em_level->lev->score : &local_player->score);
16374 DrawGameValue_Score(*score);
16378 void RaiseScore(int value)
16380 local_player->score += value;
16383 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16385 DisplayGameControlValues();
16387 DrawGameValue_Score(local_player->score);
16391 void RaiseScoreElement(int element)
16396 case EL_BD_DIAMOND:
16397 case EL_EMERALD_YELLOW:
16398 case EL_EMERALD_RED:
16399 case EL_EMERALD_PURPLE:
16400 case EL_SP_INFOTRON:
16401 RaiseScore(level.score[SC_EMERALD]);
16404 RaiseScore(level.score[SC_DIAMOND]);
16407 RaiseScore(level.score[SC_CRYSTAL]);
16410 RaiseScore(level.score[SC_PEARL]);
16413 case EL_BD_BUTTERFLY:
16414 case EL_SP_ELECTRON:
16415 RaiseScore(level.score[SC_BUG]);
16418 case EL_BD_FIREFLY:
16419 case EL_SP_SNIKSNAK:
16420 RaiseScore(level.score[SC_SPACESHIP]);
16423 case EL_DARK_YAMYAM:
16424 RaiseScore(level.score[SC_YAMYAM]);
16427 RaiseScore(level.score[SC_ROBOT]);
16430 RaiseScore(level.score[SC_PACMAN]);
16433 RaiseScore(level.score[SC_NUT]);
16436 case EL_EM_DYNAMITE:
16437 case EL_SP_DISK_RED:
16438 case EL_DYNABOMB_INCREASE_NUMBER:
16439 case EL_DYNABOMB_INCREASE_SIZE:
16440 case EL_DYNABOMB_INCREASE_POWER:
16441 RaiseScore(level.score[SC_DYNAMITE]);
16443 case EL_SHIELD_NORMAL:
16444 case EL_SHIELD_DEADLY:
16445 RaiseScore(level.score[SC_SHIELD]);
16447 case EL_EXTRA_TIME:
16448 RaiseScore(level.extra_time_score);
16462 case EL_DC_KEY_WHITE:
16463 RaiseScore(level.score[SC_KEY]);
16466 RaiseScore(element_info[element].collect_score);
16471 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16473 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16475 #if defined(NETWORK_AVALIABLE)
16476 if (options.network)
16477 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16486 FadeSkipNextFadeIn();
16488 fading = fading_none;
16492 OpenDoor(DOOR_CLOSE_1);
16495 game_status = GAME_MODE_MAIN;
16498 DrawAndFadeInMainMenu(REDRAW_FIELD);
16506 FadeOut(REDRAW_FIELD);
16509 game_status = GAME_MODE_MAIN;
16511 DrawAndFadeInMainMenu(REDRAW_FIELD);
16515 else /* continue playing the game */
16517 if (tape.playing && tape.deactivate_display)
16518 TapeDeactivateDisplayOff(TRUE);
16520 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16522 if (tape.playing && tape.deactivate_display)
16523 TapeDeactivateDisplayOn();
16527 void RequestQuitGame(boolean ask_if_really_quit)
16529 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16530 boolean skip_request = AllPlayersGone || quick_quit;
16532 RequestQuitGameExt(skip_request, quick_quit,
16533 "Do you really want to quit the game ?");
16537 /* ------------------------------------------------------------------------- */
16538 /* random generator functions */
16539 /* ------------------------------------------------------------------------- */
16541 unsigned int InitEngineRandom_RND(int seed)
16543 game.num_random_calls = 0;
16546 unsigned int rnd_seed = InitEngineRandom(seed);
16548 printf("::: START RND: %d\n", rnd_seed);
16553 return InitEngineRandom(seed);
16559 unsigned int RND(int max)
16563 game.num_random_calls++;
16565 return GetEngineRandom(max);
16572 /* ------------------------------------------------------------------------- */
16573 /* game engine snapshot handling functions */
16574 /* ------------------------------------------------------------------------- */
16576 struct EngineSnapshotInfo
16578 /* runtime values for custom element collect score */
16579 int collect_score[NUM_CUSTOM_ELEMENTS];
16581 /* runtime values for group element choice position */
16582 int choice_pos[NUM_GROUP_ELEMENTS];
16584 /* runtime values for belt position animations */
16585 int belt_graphic[4][NUM_BELT_PARTS];
16586 int belt_anim_mode[4][NUM_BELT_PARTS];
16589 static struct EngineSnapshotInfo engine_snapshot_rnd;
16590 static char *snapshot_level_identifier = NULL;
16591 static int snapshot_level_nr = -1;
16593 static void SaveEngineSnapshotValues_RND()
16595 static int belt_base_active_element[4] =
16597 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16598 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16599 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16600 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16604 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16606 int element = EL_CUSTOM_START + i;
16608 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16611 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16613 int element = EL_GROUP_START + i;
16615 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16618 for (i = 0; i < 4; i++)
16620 for (j = 0; j < NUM_BELT_PARTS; j++)
16622 int element = belt_base_active_element[i] + j;
16623 int graphic = el2img(element);
16624 int anim_mode = graphic_info[graphic].anim_mode;
16626 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16627 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16632 static void LoadEngineSnapshotValues_RND()
16634 unsigned int num_random_calls = game.num_random_calls;
16637 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16639 int element = EL_CUSTOM_START + i;
16641 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16644 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16646 int element = EL_GROUP_START + i;
16648 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16651 for (i = 0; i < 4; i++)
16653 for (j = 0; j < NUM_BELT_PARTS; j++)
16655 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16656 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16658 graphic_info[graphic].anim_mode = anim_mode;
16662 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16664 InitRND(tape.random_seed);
16665 for (i = 0; i < num_random_calls; i++)
16669 if (game.num_random_calls != num_random_calls)
16671 Error(ERR_INFO, "number of random calls out of sync");
16672 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16673 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16674 Error(ERR_EXIT, "this should not happen -- please debug");
16678 void SaveEngineSnapshot()
16680 /* do not save snapshots from editor */
16681 if (level_editor_test_game)
16684 /* free previous snapshot buffers, if needed */
16685 FreeEngineSnapshotBuffers();
16687 /* copy some special values to a structure better suited for the snapshot */
16689 SaveEngineSnapshotValues_RND();
16690 SaveEngineSnapshotValues_EM();
16691 SaveEngineSnapshotValues_SP();
16693 /* save values stored in special snapshot structure */
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16696 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16699 /* save further RND engine values */
16701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16703 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16706 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16712 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16716 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16717 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16718 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16720 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16722 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16724 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16725 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16727 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16728 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16729 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16730 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16731 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16732 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16733 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16734 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16735 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16736 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16737 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16738 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16739 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16740 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16741 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16742 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16743 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16744 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16746 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16747 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16749 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16750 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16751 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16753 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16754 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16756 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16757 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16758 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16759 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16760 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16762 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16763 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16765 /* save level identification information */
16767 setString(&snapshot_level_identifier, leveldir_current->identifier);
16768 snapshot_level_nr = level_nr;
16771 ListNode *node = engine_snapshot_list_rnd;
16774 while (node != NULL)
16776 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16781 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16785 void LoadEngineSnapshot()
16787 /* restore generically stored snapshot buffers */
16789 LoadEngineSnapshotBuffers();
16791 /* restore special values from snapshot structure */
16793 LoadEngineSnapshotValues_RND();
16794 LoadEngineSnapshotValues_EM();
16795 LoadEngineSnapshotValues_SP();
16798 boolean CheckEngineSnapshot()
16800 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16801 snapshot_level_nr == level_nr);
16805 /* ---------- new game button stuff ---------------------------------------- */
16813 } gamebutton_info[NUM_GAME_BUTTONS] =
16816 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16817 GAME_CTRL_ID_STOP, "stop game"
16820 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16821 GAME_CTRL_ID_PAUSE, "pause game"
16824 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16825 GAME_CTRL_ID_PLAY, "play game"
16828 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16829 SOUND_CTRL_ID_MUSIC, "background music on/off"
16832 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16833 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16836 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16837 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16841 void CreateGameButtons()
16845 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16847 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16848 struct Rect *pos = gamebutton_info[i].pos;
16849 struct GadgetInfo *gi;
16852 unsigned int event_mask;
16853 int gd_x = gfx->src_x;
16854 int gd_y = gfx->src_y;
16855 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16856 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16857 int gd_xa = gfx->src_x + gfx->active_xoffset;
16858 int gd_ya = gfx->src_y + gfx->active_yoffset;
16859 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16860 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16863 if (id == GAME_CTRL_ID_STOP ||
16864 id == GAME_CTRL_ID_PAUSE ||
16865 id == GAME_CTRL_ID_PLAY)
16867 button_type = GD_TYPE_NORMAL_BUTTON;
16869 event_mask = GD_EVENT_RELEASED;
16873 button_type = GD_TYPE_CHECK_BUTTON;
16875 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16876 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16877 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16878 event_mask = GD_EVENT_PRESSED;
16881 gi = CreateGadget(GDI_CUSTOM_ID, id,
16882 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16883 GDI_X, DX + pos->x,
16884 GDI_Y, DY + pos->y,
16885 GDI_WIDTH, gfx->width,
16886 GDI_HEIGHT, gfx->height,
16887 GDI_TYPE, button_type,
16888 GDI_STATE, GD_BUTTON_UNPRESSED,
16889 GDI_CHECKED, checked,
16890 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16891 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16892 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16893 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16894 GDI_DIRECT_DRAW, FALSE,
16895 GDI_EVENT_MASK, event_mask,
16896 GDI_CALLBACK_ACTION, HandleGameButtons,
16900 Error(ERR_EXIT, "cannot create gadget");
16902 game_gadget[id] = gi;
16906 void FreeGameButtons()
16910 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16911 FreeGadget(game_gadget[i]);
16914 static void MapGameButtons()
16918 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16919 MapGadget(game_gadget[i]);
16922 void UnmapGameButtons()
16926 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16927 UnmapGadget(game_gadget[i]);
16930 void RedrawGameButtons()
16934 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16935 RedrawGadget(game_gadget[i]);
16938 static void HandleGameButtonsExt(int id)
16940 if (game_status != GAME_MODE_PLAYING)
16945 case GAME_CTRL_ID_STOP:
16949 RequestQuitGame(TRUE);
16952 case GAME_CTRL_ID_PAUSE:
16953 if (options.network)
16955 #if defined(NETWORK_AVALIABLE)
16957 SendToServer_ContinuePlaying();
16959 SendToServer_PausePlaying();
16963 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16966 case GAME_CTRL_ID_PLAY:
16969 #if defined(NETWORK_AVALIABLE)
16970 if (options.network)
16971 SendToServer_ContinuePlaying();
16975 tape.pausing = FALSE;
16976 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16981 case SOUND_CTRL_ID_MUSIC:
16982 if (setup.sound_music)
16984 setup.sound_music = FALSE;
16988 else if (audio.music_available)
16990 setup.sound = setup.sound_music = TRUE;
16992 SetAudioMode(setup.sound);
16998 case SOUND_CTRL_ID_LOOPS:
16999 if (setup.sound_loops)
17000 setup.sound_loops = FALSE;
17001 else if (audio.loops_available)
17003 setup.sound = setup.sound_loops = TRUE;
17005 SetAudioMode(setup.sound);
17009 case SOUND_CTRL_ID_SIMPLE:
17010 if (setup.sound_simple)
17011 setup.sound_simple = FALSE;
17012 else if (audio.sound_available)
17014 setup.sound = setup.sound_simple = TRUE;
17016 SetAudioMode(setup.sound);
17025 static void HandleGameButtons(struct GadgetInfo *gi)
17027 HandleGameButtonsExt(gi->custom_id);
17030 void HandleSoundButtonKeys(Key key)
17033 if (key == setup.shortcut.sound_simple)
17034 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17035 else if (key == setup.shortcut.sound_loops)
17036 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17037 else if (key == setup.shortcut.sound_music)
17038 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17040 if (key == setup.shortcut.sound_simple)
17041 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17042 else if (key == setup.shortcut.sound_loops)
17043 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17044 else if (key == setup.shortcut.sound_music)
17045 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);