1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbled(x, y) \
85 DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y) \
87 DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_FRAME 35
198 #define GAME_PANEL_SHIELD_NORMAL 36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME 37
200 #define GAME_PANEL_SHIELD_DEADLY 38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME 39
202 #define GAME_PANEL_EXIT 40
203 #define GAME_PANEL_EMC_MAGIC_BALL 41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH 43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME 44
207 #define GAME_PANEL_TIMEGATE_SWITCH 45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 46
209 #define GAME_PANEL_SWITCHGATE_SWITCH 47
210 #define GAME_PANEL_EMC_LENSES 48
211 #define GAME_PANEL_EMC_LENSES_TIME 49
212 #define GAME_PANEL_EMC_MAGNIFIER 50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME 51
214 #define GAME_PANEL_BALLOON_SWITCH 52
215 #define GAME_PANEL_DYNABOMB_NUMBER 53
216 #define GAME_PANEL_DYNABOMB_SIZE 54
217 #define GAME_PANEL_DYNABOMB_POWER 55
218 #define GAME_PANEL_PENGUINS 56
219 #define GAME_PANEL_SOKOBAN_OBJECTS 57
220 #define GAME_PANEL_SOKOBAN_FIELDS 58
221 #define GAME_PANEL_ROBOT_WHEEL 59
222 #define GAME_PANEL_CONVEYOR_BELT_1 60
223 #define GAME_PANEL_CONVEYOR_BELT_2 61
224 #define GAME_PANEL_CONVEYOR_BELT_3 62
225 #define GAME_PANEL_CONVEYOR_BELT_4 63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 67
230 #define GAME_PANEL_MAGIC_WALL 68
231 #define GAME_PANEL_MAGIC_WALL_TIME 69
232 #define GAME_PANEL_GRAVITY_STATE 70
233 #define GAME_PANEL_GRAPHIC_1 71
234 #define GAME_PANEL_GRAPHIC_2 72
235 #define GAME_PANEL_GRAPHIC_3 73
236 #define GAME_PANEL_GRAPHIC_4 74
237 #define GAME_PANEL_GRAPHIC_5 75
238 #define GAME_PANEL_GRAPHIC_6 76
239 #define GAME_PANEL_GRAPHIC_7 77
240 #define GAME_PANEL_GRAPHIC_8 78
241 #define GAME_PANEL_ELEMENT_1 79
242 #define GAME_PANEL_ELEMENT_2 80
243 #define GAME_PANEL_ELEMENT_3 81
244 #define GAME_PANEL_ELEMENT_4 82
245 #define GAME_PANEL_ELEMENT_5 83
246 #define GAME_PANEL_ELEMENT_6 84
247 #define GAME_PANEL_ELEMENT_7 85
248 #define GAME_PANEL_ELEMENT_8 86
249 #define GAME_PANEL_ELEMENT_COUNT_1 87
250 #define GAME_PANEL_ELEMENT_COUNT_2 88
251 #define GAME_PANEL_ELEMENT_COUNT_3 89
252 #define GAME_PANEL_ELEMENT_COUNT_4 90
253 #define GAME_PANEL_ELEMENT_COUNT_5 91
254 #define GAME_PANEL_ELEMENT_COUNT_6 92
255 #define GAME_PANEL_ELEMENT_COUNT_7 93
256 #define GAME_PANEL_ELEMENT_COUNT_8 94
257 #define GAME_PANEL_CE_SCORE_1 95
258 #define GAME_PANEL_CE_SCORE_2 96
259 #define GAME_PANEL_CE_SCORE_3 97
260 #define GAME_PANEL_CE_SCORE_4 98
261 #define GAME_PANEL_CE_SCORE_5 99
262 #define GAME_PANEL_CE_SCORE_6 100
263 #define GAME_PANEL_CE_SCORE_7 101
264 #define GAME_PANEL_CE_SCORE_8 102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT 109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT 110
273 #define GAME_PANEL_PLAYER_NAME 111
274 #define GAME_PANEL_LEVEL_NAME 112
275 #define GAME_PANEL_LEVEL_AUTHOR 113
277 #define NUM_GAME_PANEL_CONTROLS 114
279 struct GamePanelOrderInfo
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
287 struct GamePanelControlInfo
291 struct TextPosInfo *pos;
294 int value, last_value;
295 int frame, last_frame;
300 static struct GamePanelControlInfo game_panel_controls[] =
303 GAME_PANEL_LEVEL_NUMBER,
304 &game.panel.level_number,
313 GAME_PANEL_INVENTORY_COUNT,
314 &game.panel.inventory_count,
318 GAME_PANEL_INVENTORY_FIRST_1,
319 &game.panel.inventory_first[0],
323 GAME_PANEL_INVENTORY_FIRST_2,
324 &game.panel.inventory_first[1],
328 GAME_PANEL_INVENTORY_FIRST_3,
329 &game.panel.inventory_first[2],
333 GAME_PANEL_INVENTORY_FIRST_4,
334 &game.panel.inventory_first[3],
338 GAME_PANEL_INVENTORY_FIRST_5,
339 &game.panel.inventory_first[4],
343 GAME_PANEL_INVENTORY_FIRST_6,
344 &game.panel.inventory_first[5],
348 GAME_PANEL_INVENTORY_FIRST_7,
349 &game.panel.inventory_first[6],
353 GAME_PANEL_INVENTORY_FIRST_8,
354 &game.panel.inventory_first[7],
358 GAME_PANEL_INVENTORY_LAST_1,
359 &game.panel.inventory_last[0],
363 GAME_PANEL_INVENTORY_LAST_2,
364 &game.panel.inventory_last[1],
368 GAME_PANEL_INVENTORY_LAST_3,
369 &game.panel.inventory_last[2],
373 GAME_PANEL_INVENTORY_LAST_4,
374 &game.panel.inventory_last[3],
378 GAME_PANEL_INVENTORY_LAST_5,
379 &game.panel.inventory_last[4],
383 GAME_PANEL_INVENTORY_LAST_6,
384 &game.panel.inventory_last[5],
388 GAME_PANEL_INVENTORY_LAST_7,
389 &game.panel.inventory_last[6],
393 GAME_PANEL_INVENTORY_LAST_8,
394 &game.panel.inventory_last[7],
438 GAME_PANEL_KEY_WHITE,
439 &game.panel.key_white,
443 GAME_PANEL_KEY_WHITE_COUNT,
444 &game.panel.key_white_count,
453 GAME_PANEL_HIGHSCORE,
454 &game.panel.highscore,
483 GAME_PANEL_SHIELD_NORMAL,
484 &game.panel.shield_normal,
488 GAME_PANEL_SHIELD_NORMAL_TIME,
489 &game.panel.shield_normal_time,
493 GAME_PANEL_SHIELD_DEADLY,
494 &game.panel.shield_deadly,
498 GAME_PANEL_SHIELD_DEADLY_TIME,
499 &game.panel.shield_deadly_time,
508 GAME_PANEL_EMC_MAGIC_BALL,
509 &game.panel.emc_magic_ball,
513 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514 &game.panel.emc_magic_ball_switch,
518 GAME_PANEL_LIGHT_SWITCH,
519 &game.panel.light_switch,
523 GAME_PANEL_LIGHT_SWITCH_TIME,
524 &game.panel.light_switch_time,
528 GAME_PANEL_TIMEGATE_SWITCH,
529 &game.panel.timegate_switch,
533 GAME_PANEL_TIMEGATE_SWITCH_TIME,
534 &game.panel.timegate_switch_time,
538 GAME_PANEL_SWITCHGATE_SWITCH,
539 &game.panel.switchgate_switch,
543 GAME_PANEL_EMC_LENSES,
544 &game.panel.emc_lenses,
548 GAME_PANEL_EMC_LENSES_TIME,
549 &game.panel.emc_lenses_time,
553 GAME_PANEL_EMC_MAGNIFIER,
554 &game.panel.emc_magnifier,
558 GAME_PANEL_EMC_MAGNIFIER_TIME,
559 &game.panel.emc_magnifier_time,
563 GAME_PANEL_BALLOON_SWITCH,
564 &game.panel.balloon_switch,
568 GAME_PANEL_DYNABOMB_NUMBER,
569 &game.panel.dynabomb_number,
573 GAME_PANEL_DYNABOMB_SIZE,
574 &game.panel.dynabomb_size,
578 GAME_PANEL_DYNABOMB_POWER,
579 &game.panel.dynabomb_power,
584 &game.panel.penguins,
588 GAME_PANEL_SOKOBAN_OBJECTS,
589 &game.panel.sokoban_objects,
593 GAME_PANEL_SOKOBAN_FIELDS,
594 &game.panel.sokoban_fields,
598 GAME_PANEL_ROBOT_WHEEL,
599 &game.panel.robot_wheel,
603 GAME_PANEL_CONVEYOR_BELT_1,
604 &game.panel.conveyor_belt[0],
608 GAME_PANEL_CONVEYOR_BELT_2,
609 &game.panel.conveyor_belt[1],
613 GAME_PANEL_CONVEYOR_BELT_3,
614 &game.panel.conveyor_belt[2],
618 GAME_PANEL_CONVEYOR_BELT_4,
619 &game.panel.conveyor_belt[3],
623 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624 &game.panel.conveyor_belt_switch[0],
628 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629 &game.panel.conveyor_belt_switch[1],
633 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634 &game.panel.conveyor_belt_switch[2],
638 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639 &game.panel.conveyor_belt_switch[3],
643 GAME_PANEL_MAGIC_WALL,
644 &game.panel.magic_wall,
648 GAME_PANEL_MAGIC_WALL_TIME,
649 &game.panel.magic_wall_time,
653 GAME_PANEL_GRAVITY_STATE,
654 &game.panel.gravity_state,
658 GAME_PANEL_GRAPHIC_1,
659 &game.panel.graphic[0],
663 GAME_PANEL_GRAPHIC_2,
664 &game.panel.graphic[1],
668 GAME_PANEL_GRAPHIC_3,
669 &game.panel.graphic[2],
673 GAME_PANEL_GRAPHIC_4,
674 &game.panel.graphic[3],
678 GAME_PANEL_GRAPHIC_5,
679 &game.panel.graphic[4],
683 GAME_PANEL_GRAPHIC_6,
684 &game.panel.graphic[5],
688 GAME_PANEL_GRAPHIC_7,
689 &game.panel.graphic[6],
693 GAME_PANEL_GRAPHIC_8,
694 &game.panel.graphic[7],
698 GAME_PANEL_ELEMENT_1,
699 &game.panel.element[0],
703 GAME_PANEL_ELEMENT_2,
704 &game.panel.element[1],
708 GAME_PANEL_ELEMENT_3,
709 &game.panel.element[2],
713 GAME_PANEL_ELEMENT_4,
714 &game.panel.element[3],
718 GAME_PANEL_ELEMENT_5,
719 &game.panel.element[4],
723 GAME_PANEL_ELEMENT_6,
724 &game.panel.element[5],
728 GAME_PANEL_ELEMENT_7,
729 &game.panel.element[6],
733 GAME_PANEL_ELEMENT_8,
734 &game.panel.element[7],
738 GAME_PANEL_ELEMENT_COUNT_1,
739 &game.panel.element_count[0],
743 GAME_PANEL_ELEMENT_COUNT_2,
744 &game.panel.element_count[1],
748 GAME_PANEL_ELEMENT_COUNT_3,
749 &game.panel.element_count[2],
753 GAME_PANEL_ELEMENT_COUNT_4,
754 &game.panel.element_count[3],
758 GAME_PANEL_ELEMENT_COUNT_5,
759 &game.panel.element_count[4],
763 GAME_PANEL_ELEMENT_COUNT_6,
764 &game.panel.element_count[5],
768 GAME_PANEL_ELEMENT_COUNT_7,
769 &game.panel.element_count[6],
773 GAME_PANEL_ELEMENT_COUNT_8,
774 &game.panel.element_count[7],
778 GAME_PANEL_CE_SCORE_1,
779 &game.panel.ce_score[0],
783 GAME_PANEL_CE_SCORE_2,
784 &game.panel.ce_score[1],
788 GAME_PANEL_CE_SCORE_3,
789 &game.panel.ce_score[2],
793 GAME_PANEL_CE_SCORE_4,
794 &game.panel.ce_score[3],
798 GAME_PANEL_CE_SCORE_5,
799 &game.panel.ce_score[4],
803 GAME_PANEL_CE_SCORE_6,
804 &game.panel.ce_score[5],
808 GAME_PANEL_CE_SCORE_7,
809 &game.panel.ce_score[6],
813 GAME_PANEL_CE_SCORE_8,
814 &game.panel.ce_score[7],
818 GAME_PANEL_CE_SCORE_1_ELEMENT,
819 &game.panel.ce_score_element[0],
823 GAME_PANEL_CE_SCORE_2_ELEMENT,
824 &game.panel.ce_score_element[1],
828 GAME_PANEL_CE_SCORE_3_ELEMENT,
829 &game.panel.ce_score_element[2],
833 GAME_PANEL_CE_SCORE_4_ELEMENT,
834 &game.panel.ce_score_element[3],
838 GAME_PANEL_CE_SCORE_5_ELEMENT,
839 &game.panel.ce_score_element[4],
843 GAME_PANEL_CE_SCORE_6_ELEMENT,
844 &game.panel.ce_score_element[5],
848 GAME_PANEL_CE_SCORE_7_ELEMENT,
849 &game.panel.ce_score_element[6],
853 GAME_PANEL_CE_SCORE_8_ELEMENT,
854 &game.panel.ce_score_element[7],
858 GAME_PANEL_PLAYER_NAME,
859 &game.panel.player_name,
863 GAME_PANEL_LEVEL_NAME,
864 &game.panel.level_name,
868 GAME_PANEL_LEVEL_AUTHOR,
869 &game.panel.level_author,
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING 3
884 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION 2
886 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF -1
890 #define INITIAL_MOVE_DELAY_ON 0
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED 32
894 #define MOVE_DELAY_NORMAL_SPEED 8
895 #define MOVE_DELAY_HIGH_SPEED 4
896 #define MOVE_DELAY_MAX_SPEED 1
898 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
901 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN (1)
907 #define MOVE_STEPSIZE_MAX (TILEX)
909 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
912 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
914 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
915 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
917 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
919 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
921 (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
923 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
926 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
928 RND((c)->delay_random))
931 #define GET_VALID_RUNTIME_ELEMENT(e) \
932 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
934 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
935 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
936 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
937 (be) + (e) - EL_SELF)
939 #define GET_PLAYER_FROM_BITS(p) \
940 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
943 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
944 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
945 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
946 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
947 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
948 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
949 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
950 RESOLVED_REFERENCE_ELEMENT(be, e) : \
953 #define CAN_GROW_INTO(e) \
954 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
957 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
968 (CAN_MOVE_INTO_ACID(e) && \
969 Feld[x][y] == EL_ACID) || \
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
973 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
975 (CAN_MOVE_INTO_ACID(e) && \
976 Feld[x][y] == EL_ACID) || \
977 (DONT_COLLIDE_WITH(e) && \
979 !PLAYER_ENEMY_PROTECTED(x, y))))
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
984 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
985 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
988 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
990 #define ANDROID_CAN_CLONE_FIELD(x, y) \
991 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
992 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1006 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1019 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1022 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1025 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1026 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1028 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1030 #define CE_ENTER_FIELD_COND(e, x, y) \
1031 (!IS_PLAYER(x, y) && \
1032 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1035 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1040 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP 0
1047 #define GAME_CTRL_ID_PAUSE 1
1048 #define GAME_CTRL_ID_PLAY 2
1049 #define SOUND_CTRL_ID_MUSIC 3
1050 #define SOUND_CTRL_ID_LOOPS 4
1051 #define SOUND_CTRL_ID_SIMPLE 5
1053 #define NUM_GAME_BUTTONS 6
1056 /* forward declaration for internal use */
1058 static void CreateField(int, int, int);
1060 static void ResetGfxAnimation(int, int);
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1097 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1099 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1101 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev) \
1105 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1107 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1109 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1155 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1157 if (recursion_loop_detected) \
1160 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1162 recursion_loop_detected = TRUE; \
1163 recursion_loop_element = (e); \
1166 recursion_loop_depth++; \
1169 #define RECURSION_LOOP_DETECTION_END() \
1171 recursion_loop_depth--; \
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1178 static int map_player_action[MAX_PLAYERS];
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after */
1183 /* a specified time, eventually calling a function when changing */
1184 /* ------------------------------------------------------------------------- */
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1204 struct ChangingElementInfo
1209 void (*pre_change_function)(int x, int y);
1210 void (*change_function)(int x, int y);
1211 void (*post_change_function)(int x, int y);
1214 static struct ChangingElementInfo change_delay_list[] =
1249 EL_STEEL_EXIT_OPENING,
1257 EL_STEEL_EXIT_CLOSING,
1258 EL_STEEL_EXIT_CLOSED,
1285 EL_EM_STEEL_EXIT_OPENING,
1286 EL_EM_STEEL_EXIT_OPEN,
1293 EL_EM_STEEL_EXIT_CLOSING,
1297 EL_EM_STEEL_EXIT_CLOSED,
1321 EL_SWITCHGATE_OPENING,
1329 EL_SWITCHGATE_CLOSING,
1330 EL_SWITCHGATE_CLOSED,
1337 EL_TIMEGATE_OPENING,
1345 EL_TIMEGATE_CLOSING,
1354 EL_ACID_SPLASH_LEFT,
1362 EL_ACID_SPLASH_RIGHT,
1371 EL_SP_BUGGY_BASE_ACTIVATING,
1378 EL_SP_BUGGY_BASE_ACTIVATING,
1379 EL_SP_BUGGY_BASE_ACTIVE,
1386 EL_SP_BUGGY_BASE_ACTIVE,
1410 EL_ROBOT_WHEEL_ACTIVE,
1418 EL_TIMEGATE_SWITCH_ACTIVE,
1426 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427 EL_DC_TIMEGATE_SWITCH,
1434 EL_EMC_MAGIC_BALL_ACTIVE,
1435 EL_EMC_MAGIC_BALL_ACTIVE,
1442 EL_EMC_SPRING_BUMPER_ACTIVE,
1443 EL_EMC_SPRING_BUMPER,
1450 EL_DIAGONAL_SHRINKING,
1458 EL_DIAGONAL_GROWING,
1479 int push_delay_fixed, push_delay_random;
1483 { EL_SPRING, 0, 0 },
1484 { EL_BALLOON, 0, 0 },
1486 { EL_SOKOBAN_OBJECT, 2, 0 },
1487 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1488 { EL_SATELLITE, 2, 0 },
1489 { EL_SP_DISK_YELLOW, 2, 0 },
1491 { EL_UNDEFINED, 0, 0 },
1499 move_stepsize_list[] =
1501 { EL_AMOEBA_DROP, 2 },
1502 { EL_AMOEBA_DROPPING, 2 },
1503 { EL_QUICKSAND_FILLING, 1 },
1504 { EL_QUICKSAND_EMPTYING, 1 },
1505 { EL_QUICKSAND_FAST_FILLING, 2 },
1506 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1507 { EL_MAGIC_WALL_FILLING, 2 },
1508 { EL_MAGIC_WALL_EMPTYING, 2 },
1509 { EL_BD_MAGIC_WALL_FILLING, 2 },
1510 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1511 { EL_DC_MAGIC_WALL_FILLING, 2 },
1512 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1514 { EL_UNDEFINED, 0 },
1522 collect_count_list[] =
1525 { EL_BD_DIAMOND, 1 },
1526 { EL_EMERALD_YELLOW, 1 },
1527 { EL_EMERALD_RED, 1 },
1528 { EL_EMERALD_PURPLE, 1 },
1530 { EL_SP_INFOTRON, 1 },
1534 { EL_UNDEFINED, 0 },
1542 access_direction_list[] =
1544 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1546 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1547 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1548 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1549 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1550 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1551 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1552 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1553 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1554 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1556 { EL_SP_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_PORT_UP, MV_DOWN },
1559 { EL_SP_PORT_DOWN, MV_UP },
1560 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1561 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1562 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1567 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1568 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1569 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1570 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1571 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1572 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1573 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1574 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1576 { EL_UNDEFINED, MV_NONE }
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1581 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1584 IS_JUST_CHANGING(x, y))
1586 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1594 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1595 (y) >= 0 && (y) <= lev_fieldy - 1; \
1596 (y) += playfield_scan_delta_y) \
1597 for ((x) = playfield_scan_start_x; \
1598 (x) >= 0 && (x) <= lev_fieldx - 1; \
1599 (x) += playfield_scan_delta_x)
1602 void DEBUG_SetMaximumDynamite()
1606 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608 local_player->inventory_element[local_player->inventory_size++] =
1613 static void InitPlayfieldScanModeVars()
1615 if (game.use_reverse_scan_direction)
1617 playfield_scan_start_x = lev_fieldx - 1;
1618 playfield_scan_start_y = lev_fieldy - 1;
1620 playfield_scan_delta_x = -1;
1621 playfield_scan_delta_y = -1;
1625 playfield_scan_start_x = 0;
1626 playfield_scan_start_y = 0;
1628 playfield_scan_delta_x = 1;
1629 playfield_scan_delta_y = 1;
1633 static void InitPlayfieldScanMode(int mode)
1635 game.use_reverse_scan_direction =
1636 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1638 InitPlayfieldScanModeVars();
1641 static int get_move_delay_from_stepsize(int move_stepsize)
1644 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1646 /* make sure that stepsize value is always a power of 2 */
1647 move_stepsize = (1 << log_2(move_stepsize));
1649 return TILEX / move_stepsize;
1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1655 int player_nr = player->index_nr;
1656 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1659 /* do no immediately change move delay -- the player might just be moving */
1660 player->move_delay_value_next = move_delay;
1662 /* information if player can move must be set separately */
1663 player->cannot_move = cannot_move;
1667 player->move_delay = game.initial_move_delay[player_nr];
1668 player->move_delay_value = game.initial_move_delay_value[player_nr];
1670 player->move_delay_value_next = -1;
1672 player->move_delay_reset_counter = 0;
1676 void GetPlayerConfig()
1678 GameFrameDelay = setup.game_frame_delay;
1680 if (!audio.sound_available)
1681 setup.sound_simple = FALSE;
1683 if (!audio.loops_available)
1684 setup.sound_loops = FALSE;
1686 if (!audio.music_available)
1687 setup.sound_music = FALSE;
1689 if (!video.fullscreen_available)
1690 setup.fullscreen = FALSE;
1692 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1694 SetAudioMode(setup.sound);
1698 int GetElementFromGroupElement(int element)
1700 if (IS_GROUP_ELEMENT(element))
1702 struct ElementGroupInfo *group = element_info[element].group;
1703 int last_anim_random_frame = gfx.anim_random_frame;
1706 if (group->choice_mode == ANIM_RANDOM)
1707 gfx.anim_random_frame = RND(group->num_elements_resolved);
1709 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 group->choice_mode, 0,
1713 if (group->choice_mode == ANIM_RANDOM)
1714 gfx.anim_random_frame = last_anim_random_frame;
1716 group->choice_pos++;
1718 element = group->element_resolved[element_pos];
1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1726 if (element == EL_SP_MURPHY)
1730 if (stored_player[0].present)
1732 Feld[x][y] = EL_SP_MURPHY_CLONE;
1738 stored_player[0].initial_element = element;
1739 stored_player[0].use_murphy = TRUE;
1741 if (!level.use_artwork_element[0])
1742 stored_player[0].artwork_element = EL_SP_MURPHY;
1745 Feld[x][y] = EL_PLAYER_1;
1751 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752 int jx = player->jx, jy = player->jy;
1754 player->present = TRUE;
1756 player->block_last_field = (element == EL_SP_MURPHY ?
1757 level.sp_block_last_field :
1758 level.block_last_field);
1760 /* ---------- initialize player's last field block delay --------------- */
1762 /* always start with reliable default value (no adjustment needed) */
1763 player->block_delay_adjustment = 0;
1765 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766 if (player->block_last_field && element == EL_SP_MURPHY)
1767 player->block_delay_adjustment = 1;
1769 /* special case 2: in game engines before 3.1.1, blocking was different */
1770 if (game.use_block_last_field_bug)
1771 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1773 if (!options.network || player->connected)
1775 player->active = TRUE;
1777 /* remove potentially duplicate players */
1778 if (StorePlayer[jx][jy] == Feld[x][y])
1779 StorePlayer[jx][jy] = 0;
1781 StorePlayer[x][y] = Feld[x][y];
1785 printf("Player %d activated.\n", player->element_nr);
1786 printf("[Local player is %d and currently %s.]\n",
1787 local_player->element_nr,
1788 local_player->active ? "active" : "not active");
1792 Feld[x][y] = EL_EMPTY;
1794 player->jx = player->last_jx = x;
1795 player->jy = player->last_jy = y;
1798 #if USE_PLAYER_REANIMATION
1801 int player_nr = GET_PLAYER_NR(element);
1802 struct PlayerInfo *player = &stored_player[player_nr];
1804 if (player->active && player->killed)
1805 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1810 static void InitField(int x, int y, boolean init_game)
1812 int element = Feld[x][y];
1821 InitPlayerField(x, y, element, init_game);
1824 case EL_SOKOBAN_FIELD_PLAYER:
1825 element = Feld[x][y] = EL_PLAYER_1;
1826 InitField(x, y, init_game);
1828 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829 InitField(x, y, init_game);
1832 case EL_SOKOBAN_FIELD_EMPTY:
1833 local_player->sokobanfields_still_needed++;
1837 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1855 case EL_SPACESHIP_RIGHT:
1856 case EL_SPACESHIP_UP:
1857 case EL_SPACESHIP_LEFT:
1858 case EL_SPACESHIP_DOWN:
1859 case EL_BD_BUTTERFLY:
1860 case EL_BD_BUTTERFLY_RIGHT:
1861 case EL_BD_BUTTERFLY_UP:
1862 case EL_BD_BUTTERFLY_LEFT:
1863 case EL_BD_BUTTERFLY_DOWN:
1865 case EL_BD_FIREFLY_RIGHT:
1866 case EL_BD_FIREFLY_UP:
1867 case EL_BD_FIREFLY_LEFT:
1868 case EL_BD_FIREFLY_DOWN:
1869 case EL_PACMAN_RIGHT:
1871 case EL_PACMAN_LEFT:
1872 case EL_PACMAN_DOWN:
1874 case EL_YAMYAM_LEFT:
1875 case EL_YAMYAM_RIGHT:
1877 case EL_YAMYAM_DOWN:
1878 case EL_DARK_YAMYAM:
1881 case EL_SP_SNIKSNAK:
1882 case EL_SP_ELECTRON:
1891 case EL_AMOEBA_FULL:
1896 case EL_AMOEBA_DROP:
1897 if (y == lev_fieldy - 1)
1899 Feld[x][y] = EL_AMOEBA_GROWING;
1900 Store[x][y] = EL_AMOEBA_WET;
1904 case EL_DYNAMITE_ACTIVE:
1905 case EL_SP_DISK_RED_ACTIVE:
1906 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910 MovDelay[x][y] = 96;
1913 case EL_EM_DYNAMITE_ACTIVE:
1914 MovDelay[x][y] = 32;
1918 local_player->lights_still_needed++;
1922 local_player->friends_still_needed++;
1927 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1948 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1950 game.belt_dir[belt_nr] = belt_dir;
1951 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1953 else /* more than one switch -- set it like the first switch */
1955 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1963 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1966 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1968 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1972 case EL_LIGHT_SWITCH_ACTIVE:
1974 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1977 case EL_INVISIBLE_STEELWALL:
1978 case EL_INVISIBLE_WALL:
1979 case EL_INVISIBLE_SAND:
1980 if (game.light_time_left > 0 ||
1981 game.lenses_time_left > 0)
1982 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1985 case EL_EMC_MAGIC_BALL:
1986 if (game.ball_state)
1987 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1990 case EL_EMC_MAGIC_BALL_SWITCH:
1991 if (game.ball_state)
1992 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1995 case EL_TRIGGER_PLAYER:
1996 case EL_TRIGGER_ELEMENT:
1997 case EL_TRIGGER_CE_VALUE:
1998 case EL_TRIGGER_CE_SCORE:
2000 case EL_ANY_ELEMENT:
2001 case EL_CURRENT_CE_VALUE:
2002 case EL_CURRENT_CE_SCORE:
2019 /* reference elements should not be used on the playfield */
2020 Feld[x][y] = EL_EMPTY;
2024 if (IS_CUSTOM_ELEMENT(element))
2026 if (CAN_MOVE(element))
2029 #if USE_NEW_CUSTOM_VALUE
2030 if (!element_info[element].use_last_ce_value || init_game)
2031 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2034 else if (IS_GROUP_ELEMENT(element))
2036 Feld[x][y] = GetElementFromGroupElement(element);
2038 InitField(x, y, init_game);
2045 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2050 InitField(x, y, init_game);
2052 /* not needed to call InitMovDir() -- already done by InitField()! */
2053 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054 CAN_MOVE(Feld[x][y]))
2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2060 int old_element = Feld[x][y];
2062 InitField(x, y, init_game);
2064 /* not needed to call InitMovDir() -- already done by InitField()! */
2065 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066 CAN_MOVE(old_element) &&
2067 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2070 /* this case is in fact a combination of not less than three bugs:
2071 first, it calls InitMovDir() for elements that can move, although this is
2072 already done by InitField(); then, it checks the element that was at this
2073 field _before_ the call to InitField() (which can change it); lastly, it
2074 was not called for "mole with direction" elements, which were treated as
2075 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2081 static int get_key_element_from_nr(int key_nr)
2083 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 EL_EM_KEY_1 : EL_KEY_1);
2087 return key_base_element + key_nr;
2090 static int get_next_dropped_element(struct PlayerInfo *player)
2092 return (player->inventory_size > 0 ?
2093 player->inventory_element[player->inventory_size - 1] :
2094 player->inventory_infinite_element != EL_UNDEFINED ?
2095 player->inventory_infinite_element :
2096 player->dynabombs_left > 0 ?
2097 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2103 /* pos >= 0: get element from bottom of the stack;
2104 pos < 0: get element from top of the stack */
2108 int min_inventory_size = -pos;
2109 int inventory_pos = player->inventory_size - min_inventory_size;
2110 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2112 return (player->inventory_size >= min_inventory_size ?
2113 player->inventory_element[inventory_pos] :
2114 player->inventory_infinite_element != EL_UNDEFINED ?
2115 player->inventory_infinite_element :
2116 player->dynabombs_left >= min_dynabombs_left ?
2117 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 int min_dynabombs_left = pos + 1;
2123 int min_inventory_size = pos + 1 - player->dynabombs_left;
2124 int inventory_pos = pos - player->dynabombs_left;
2126 return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 player->inventory_infinite_element :
2128 player->dynabombs_left >= min_dynabombs_left ?
2129 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 player->inventory_size >= min_inventory_size ?
2131 player->inventory_element[inventory_pos] :
2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2138 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2142 if (gpo1->sort_priority != gpo2->sort_priority)
2143 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2145 compare_result = gpo1->nr - gpo2->nr;
2147 return compare_result;
2150 void InitGameControlValues()
2154 for (i = 0; game_panel_controls[i].nr != -1; i++)
2156 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158 struct TextPosInfo *pos = gpc->pos;
2160 int type = gpc->type;
2164 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165 Error(ERR_EXIT, "this should not happen -- please debug");
2168 /* force update of game controls after initialization */
2169 gpc->value = gpc->last_value = -1;
2170 gpc->frame = gpc->last_frame = -1;
2171 gpc->gfx_frame = -1;
2173 /* determine panel value width for later calculation of alignment */
2174 if (type == TYPE_INTEGER || type == TYPE_STRING)
2176 pos->width = pos->size * getFontWidth(pos->font);
2177 pos->height = getFontHeight(pos->font);
2179 else if (type == TYPE_ELEMENT)
2181 pos->width = pos->size;
2182 pos->height = pos->size;
2185 /* fill structure for game panel draw order */
2187 gpo->sort_priority = pos->sort_priority;
2190 /* sort game panel controls according to sort_priority and control number */
2191 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2195 void UpdatePlayfieldElementCount()
2197 boolean use_element_count = FALSE;
2200 /* first check if it is needed at all to calculate playfield element count */
2201 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203 use_element_count = TRUE;
2205 if (!use_element_count)
2208 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209 element_info[i].element_count = 0;
2211 SCAN_PLAYFIELD(x, y)
2213 element_info[Feld[x][y]].element_count++;
2216 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218 if (IS_IN_GROUP(j, i))
2219 element_info[EL_GROUP_START + i].element_count +=
2220 element_info[j].element_count;
2223 void UpdateGameControlValues()
2226 int time = (local_player->LevelSolved ?
2227 local_player->LevelSolved_CountingTime :
2228 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->time :
2230 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 level.native_sp_level->game_sp->time_played :
2232 level.time == 0 ? TimePlayed : TimeLeft);
2233 int score = (local_player->LevelSolved ?
2234 local_player->LevelSolved_CountingScore :
2235 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 level.native_em_level->lev->score :
2237 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 level.native_sp_level->game_sp->score :
2239 local_player->score);
2240 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 level.native_em_level->lev->required :
2242 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 level.native_sp_level->game_sp->infotrons_still_needed :
2244 local_player->gems_still_needed);
2245 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 level.native_em_level->lev->required > 0 :
2247 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 local_player->gems_still_needed > 0 ||
2250 local_player->sokobanfields_still_needed > 0 ||
2251 local_player->lights_still_needed > 0);
2253 UpdatePlayfieldElementCount();
2255 /* update game panel control values */
2257 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2260 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261 for (i = 0; i < MAX_NUM_KEYS; i++)
2262 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2266 if (game.centered_player_nr == -1)
2268 for (i = 0; i < MAX_PLAYERS; i++)
2270 /* only one player in Supaplex game engine */
2271 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2274 for (k = 0; k < MAX_NUM_KEYS; k++)
2276 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2278 if (level.native_em_level->ply[i]->keys & (1 << k))
2279 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 get_key_element_from_nr(k);
2282 else if (stored_player[i].key[k])
2283 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 get_key_element_from_nr(k);
2287 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 level.native_em_level->ply[i]->dynamite;
2290 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 level.native_sp_level->game_sp->red_disk_count;
2294 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 stored_player[i].inventory_size;
2297 if (stored_player[i].num_white_keys > 0)
2298 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2301 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 stored_player[i].num_white_keys;
2307 int player_nr = game.centered_player_nr;
2309 for (k = 0; k < MAX_NUM_KEYS; k++)
2311 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2313 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 get_key_element_from_nr(k);
2317 else if (stored_player[player_nr].key[k])
2318 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 get_key_element_from_nr(k);
2322 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 level.native_em_level->ply[player_nr]->dynamite;
2325 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 level.native_sp_level->game_sp->red_disk_count;
2329 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 stored_player[player_nr].inventory_size;
2332 if (stored_player[player_nr].num_white_keys > 0)
2333 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2335 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336 stored_player[player_nr].num_white_keys;
2339 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2341 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342 get_inventory_element_from_pos(local_player, i);
2343 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344 get_inventory_element_from_pos(local_player, -i - 1);
2347 game_panel_controls[GAME_PANEL_SCORE].value = score;
2348 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2350 game_panel_controls[GAME_PANEL_TIME].value = time;
2352 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2356 game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2358 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2361 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362 local_player->shield_normal_time_left;
2363 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2366 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367 local_player->shield_deadly_time_left;
2369 game_panel_controls[GAME_PANEL_EXIT].value =
2370 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2372 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376 EL_EMC_MAGIC_BALL_SWITCH);
2378 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381 game.light_time_left;
2383 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386 game.timegate_time_left;
2388 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2391 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394 game.lenses_time_left;
2396 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399 game.magnify_time_left;
2401 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2403 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2405 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2406 EL_BALLOON_SWITCH_NONE);
2408 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409 local_player->dynabomb_count;
2410 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411 local_player->dynabomb_size;
2412 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2415 game_panel_controls[GAME_PANEL_PENGUINS].value =
2416 local_player->friends_still_needed;
2418 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419 local_player->sokobanfields_still_needed;
2420 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421 local_player->sokobanfields_still_needed;
2423 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2426 for (i = 0; i < NUM_BELTS; i++)
2428 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2435 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438 game.magic_wall_time_left;
2440 #if USE_PLAYER_GRAVITY
2441 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442 local_player->gravity;
2444 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2447 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2450 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453 game.panel.element[i].id : EL_UNDEFINED);
2455 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458 element_info[game.panel.element_count[i].id].element_count : 0);
2460 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463 element_info[game.panel.ce_score[i].id].collect_score : 0);
2465 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468 element_info[game.panel.ce_score_element[i].id].collect_score :
2471 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2475 /* update game panel control frames */
2477 for (i = 0; game_panel_controls[i].nr != -1; i++)
2479 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2481 if (gpc->type == TYPE_ELEMENT)
2483 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2485 int last_anim_random_frame = gfx.anim_random_frame;
2486 int element = gpc->value;
2487 int graphic = el2panelimg(element);
2489 if (gpc->value != gpc->last_value)
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2498 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 gpc->gfx_random = INIT_GFX_RANDOM();
2503 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 gfx.anim_random_frame = gpc->gfx_random;
2506 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 gpc->gfx_frame = element_info[element].collect_score;
2509 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2512 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 gfx.anim_random_frame = last_anim_random_frame;
2519 void DisplayGameControlValues()
2521 boolean redraw_panel = FALSE;
2524 for (i = 0; game_panel_controls[i].nr != -1; i++)
2526 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2528 if (PANEL_DEACTIVATED(gpc->pos))
2531 if (gpc->value == gpc->last_value &&
2532 gpc->frame == gpc->last_frame)
2535 redraw_panel = TRUE;
2541 /* copy default game door content to main double buffer */
2543 /* !!! CHECK AGAIN !!! */
2544 SetPanelBackground();
2545 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2546 DrawBackground(DX, DY, DXSIZE, DYSIZE);
2548 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2549 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2552 /* redraw game control buttons */
2554 RedrawGameButtons();
2560 game_status = GAME_MODE_PSEUDO_PANEL;
2563 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2565 for (i = 0; game_panel_controls[i].nr != -1; i++)
2569 int nr = game_panel_order[i].nr;
2570 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2572 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2575 struct TextPosInfo *pos = gpc->pos;
2576 int type = gpc->type;
2577 int value = gpc->value;
2578 int frame = gpc->frame;
2580 int last_value = gpc->last_value;
2581 int last_frame = gpc->last_frame;
2583 int size = pos->size;
2584 int font = pos->font;
2585 boolean draw_masked = pos->draw_masked;
2586 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2588 if (PANEL_DEACTIVATED(pos))
2592 if (value == last_value && frame == last_frame)
2596 gpc->last_value = value;
2597 gpc->last_frame = frame;
2600 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2603 if (type == TYPE_INTEGER)
2605 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2606 nr == GAME_PANEL_TIME)
2608 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2610 if (use_dynamic_size) /* use dynamic number of digits */
2612 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2613 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2614 int size2 = size1 + 1;
2615 int font1 = pos->font;
2616 int font2 = pos->font_alt;
2618 size = (value < value_change ? size1 : size2);
2619 font = (value < value_change ? font1 : font2);
2622 /* clear background if value just changed its size (dynamic digits) */
2623 if ((last_value < value_change) != (value < value_change))
2625 int width1 = size1 * getFontWidth(font1);
2626 int width2 = size2 * getFontWidth(font2);
2627 int max_width = MAX(width1, width2);
2628 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2630 pos->width = max_width;
2632 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2633 max_width, max_height);
2640 /* correct text size if "digits" is zero or less */
2642 size = strlen(int2str(value, size));
2644 /* dynamically correct text alignment */
2645 pos->width = size * getFontWidth(font);
2648 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2649 int2str(value, size), font, mask_mode);
2651 else if (type == TYPE_ELEMENT)
2653 int element, graphic;
2657 int dst_x = PANEL_XPOS(pos);
2658 int dst_y = PANEL_YPOS(pos);
2661 if (value != EL_UNDEFINED && value != EL_EMPTY)
2664 graphic = el2panelimg(value);
2666 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2669 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2673 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2676 width = graphic_info[graphic].width * size / TILESIZE;
2677 height = graphic_info[graphic].height * size / TILESIZE;
2681 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2682 dst_x - src_x, dst_y - src_y);
2683 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2688 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2693 if (value == EL_UNDEFINED || value == EL_EMPTY)
2695 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2696 graphic = el2panelimg(element);
2698 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2699 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2700 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2705 graphic = el2panelimg(value);
2707 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2710 width = graphic_info[graphic].width * size / TILESIZE;
2711 height = graphic_info[graphic].height * size / TILESIZE;
2713 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2716 else if (type == TYPE_STRING)
2718 boolean active = (value != 0);
2719 char *state_normal = "off";
2720 char *state_active = "on";
2721 char *state = (active ? state_active : state_normal);
2722 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2723 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2724 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2725 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2727 if (nr == GAME_PANEL_GRAVITY_STATE)
2729 int font1 = pos->font; /* (used for normal state) */
2730 int font2 = pos->font_alt; /* (used for active state) */
2732 int size1 = strlen(state_normal);
2733 int size2 = strlen(state_active);
2734 int width1 = size1 * getFontWidth(font1);
2735 int width2 = size2 * getFontWidth(font2);
2736 int max_width = MAX(width1, width2);
2737 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2739 pos->width = max_width;
2741 /* clear background for values that may have changed its size */
2742 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2743 max_width, max_height);
2746 font = (active ? font2 : font1);
2756 /* don't truncate output if "chars" is zero or less */
2759 /* dynamically correct text alignment */
2760 pos->width = size * getFontWidth(font);
2764 s_cut = getStringCopyN(s, size);
2766 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767 s_cut, font, mask_mode);
2773 redraw_mask |= REDRAW_DOOR_1;
2776 game_status = GAME_MODE_PLAYING;
2779 void UpdateAndDisplayGameControlValues()
2781 if (tape.warp_forward)
2784 UpdateGameControlValues();
2785 DisplayGameControlValues();
2788 void DrawGameValue_Emeralds(int value)
2790 struct TextPosInfo *pos = &game.panel.gems;
2792 int font_nr = pos->font;
2794 int font_nr = FONT_TEXT_2;
2796 int font_width = getFontWidth(font_nr);
2797 int chars = pos->size;
2800 return; /* !!! USE NEW STUFF !!! */
2803 if (PANEL_DEACTIVATED(pos))
2806 pos->width = chars * font_width;
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Dynamite(int value)
2813 struct TextPosInfo *pos = &game.panel.inventory_count;
2815 int font_nr = pos->font;
2817 int font_nr = FONT_TEXT_2;
2819 int font_width = getFontWidth(font_nr);
2820 int chars = pos->size;
2823 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2829 pos->width = chars * font_width;
2831 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2834 void DrawGameValue_Score(int value)
2836 struct TextPosInfo *pos = &game.panel.score;
2838 int font_nr = pos->font;
2840 int font_nr = FONT_TEXT_2;
2842 int font_width = getFontWidth(font_nr);
2843 int chars = pos->size;
2846 return; /* !!! USE NEW STUFF !!! */
2849 if (PANEL_DEACTIVATED(pos))
2852 pos->width = chars * font_width;
2854 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2857 void DrawGameValue_Time(int value)
2859 struct TextPosInfo *pos = &game.panel.time;
2860 static int last_value = -1;
2863 int chars = pos->size;
2865 int font1_nr = pos->font;
2866 int font2_nr = pos->font_alt;
2868 int font1_nr = FONT_TEXT_2;
2869 int font2_nr = FONT_TEXT_1;
2871 int font_nr = font1_nr;
2872 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2875 return; /* !!! USE NEW STUFF !!! */
2878 if (PANEL_DEACTIVATED(pos))
2881 if (use_dynamic_chars) /* use dynamic number of chars */
2883 chars = (value < 1000 ? chars1 : chars2);
2884 font_nr = (value < 1000 ? font1_nr : font2_nr);
2887 /* clear background if value just changed its size (dynamic chars only) */
2888 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2890 int width1 = chars1 * getFontWidth(font1_nr);
2891 int width2 = chars2 * getFontWidth(font2_nr);
2892 int max_width = MAX(width1, width2);
2893 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2895 pos->width = max_width;
2897 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2898 max_width, max_height);
2901 pos->width = chars * getFontWidth(font_nr);
2903 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2908 void DrawGameValue_Level(int value)
2910 struct TextPosInfo *pos = &game.panel.level_number;
2913 int chars = pos->size;
2915 int font1_nr = pos->font;
2916 int font2_nr = pos->font_alt;
2918 int font1_nr = FONT_TEXT_2;
2919 int font2_nr = FONT_TEXT_1;
2921 int font_nr = font1_nr;
2922 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2925 return; /* !!! USE NEW STUFF !!! */
2928 if (PANEL_DEACTIVATED(pos))
2931 if (use_dynamic_chars) /* use dynamic number of chars */
2933 chars = (level_nr < 100 ? chars1 : chars2);
2934 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2937 pos->width = chars * getFontWidth(font_nr);
2939 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2942 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2945 struct TextPosInfo *pos = &game.panel.keys;
2948 int base_key_graphic = EL_KEY_1;
2953 return; /* !!! USE NEW STUFF !!! */
2957 if (PANEL_DEACTIVATED(pos))
2962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2963 base_key_graphic = EL_EM_KEY_1;
2967 pos->width = 4 * MINI_TILEX;
2971 for (i = 0; i < MAX_NUM_KEYS; i++)
2973 /* currently only 4 of 8 possible keys are displayed */
2974 for (i = 0; i < STD_NUM_KEYS; i++)
2978 struct TextPosInfo *pos = &game.panel.key[i];
2980 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2981 int src_y = DOOR_GFX_PAGEY1 + 123;
2983 int dst_x = PANEL_XPOS(pos);
2984 int dst_y = PANEL_YPOS(pos);
2986 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2987 int dst_y = PANEL_YPOS(pos);
2991 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2992 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2994 int graphic = el2edimg(element);
2998 if (PANEL_DEACTIVATED(pos))
3003 /* masked blit with tiles from half-size scaled bitmap does not work yet
3004 (no mask bitmap created for these sizes after loading and scaling) --
3005 solution: load without creating mask, scale, then create final mask */
3007 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3008 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3013 int graphic = el2edimg(base_key_graphic + i);
3018 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3020 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3021 dst_x - src_x, dst_y - src_y);
3022 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3028 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3030 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3031 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3034 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3036 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3037 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3045 void DrawGameValue_Emeralds(int value)
3047 int font_nr = FONT_TEXT_2;
3048 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3050 if (PANEL_DEACTIVATED(game.panel.gems))
3053 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3056 void DrawGameValue_Dynamite(int value)
3058 int font_nr = FONT_TEXT_2;
3059 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3061 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3064 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3067 void DrawGameValue_Score(int value)
3069 int font_nr = FONT_TEXT_2;
3070 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3072 if (PANEL_DEACTIVATED(game.panel.score))
3075 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3078 void DrawGameValue_Time(int value)
3080 int font1_nr = FONT_TEXT_2;
3082 int font2_nr = FONT_TEXT_1;
3084 int font2_nr = FONT_LEVEL_NUMBER;
3086 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3087 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3089 if (PANEL_DEACTIVATED(game.panel.time))
3092 /* clear background if value just changed its size */
3093 if (value == 999 || value == 1000)
3094 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3097 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3099 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3102 void DrawGameValue_Level(int value)
3104 int font1_nr = FONT_TEXT_2;
3106 int font2_nr = FONT_TEXT_1;
3108 int font2_nr = FONT_LEVEL_NUMBER;
3111 if (PANEL_DEACTIVATED(game.panel.level))
3115 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3117 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3120 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3122 int base_key_graphic = EL_KEY_1;
3125 if (PANEL_DEACTIVATED(game.panel.keys))
3128 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3129 base_key_graphic = EL_EM_KEY_1;
3131 /* currently only 4 of 8 possible keys are displayed */
3132 for (i = 0; i < STD_NUM_KEYS; i++)
3134 int x = XX_KEYS + i * MINI_TILEX;
3138 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3140 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3141 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3147 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3150 int key[MAX_NUM_KEYS];
3153 /* prevent EM engine from updating time/score values parallel to GameWon() */
3154 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3155 local_player->LevelSolved)
3158 for (i = 0; i < MAX_NUM_KEYS; i++)
3159 key[i] = key_bits & (1 << i);
3161 DrawGameValue_Level(level_nr);
3163 DrawGameValue_Emeralds(emeralds);
3164 DrawGameValue_Dynamite(dynamite);
3165 DrawGameValue_Score(score);
3166 DrawGameValue_Time(time);
3168 DrawGameValue_Keys(key);
3171 void UpdateGameDoorValues()
3173 UpdateGameControlValues();
3176 void DrawGameDoorValues()
3178 DisplayGameControlValues();
3181 void DrawGameDoorValues_OLD()
3183 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3184 int dynamite_value = 0;
3185 int score_value = (local_player->LevelSolved ? local_player->score_final :
3186 local_player->score);
3187 int gems_value = local_player->gems_still_needed;
3191 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3193 DrawGameDoorValues_EM();
3198 if (game.centered_player_nr == -1)
3200 for (i = 0; i < MAX_PLAYERS; i++)
3202 for (j = 0; j < MAX_NUM_KEYS; j++)
3203 if (stored_player[i].key[j])
3204 key_bits |= (1 << j);
3206 dynamite_value += stored_player[i].inventory_size;
3211 int player_nr = game.centered_player_nr;
3213 for (i = 0; i < MAX_NUM_KEYS; i++)
3214 if (stored_player[player_nr].key[i])
3215 key_bits |= (1 << i);
3217 dynamite_value = stored_player[player_nr].inventory_size;
3220 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3226 =============================================================================
3228 -----------------------------------------------------------------------------
3229 initialize game engine due to level / tape version number
3230 =============================================================================
3233 static void InitGameEngine()
3235 int i, j, k, l, x, y;
3237 /* set game engine from tape file when re-playing, else from level file */
3238 game.engine_version = (tape.playing ? tape.engine_version :
3239 level.game_version);
3241 /* ---------------------------------------------------------------------- */
3242 /* set flags for bugs and changes according to active game engine version */
3243 /* ---------------------------------------------------------------------- */
3246 Summary of bugfix/change:
3247 Fixed handling for custom elements that change when pushed by the player.
3249 Fixed/changed in version:
3253 Before 3.1.0, custom elements that "change when pushing" changed directly
3254 after the player started pushing them (until then handled in "DigField()").
3255 Since 3.1.0, these custom elements are not changed until the "pushing"
3256 move of the element is finished (now handled in "ContinueMoving()").
3258 Affected levels/tapes:
3259 The first condition is generally needed for all levels/tapes before version
3260 3.1.0, which might use the old behaviour before it was changed; known tapes
3261 that are affected are some tapes from the level set "Walpurgis Gardens" by
3263 The second condition is an exception from the above case and is needed for
3264 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3265 above (including some development versions of 3.1.0), but before it was
3266 known that this change would break tapes like the above and was fixed in
3267 3.1.1, so that the changed behaviour was active although the engine version
3268 while recording maybe was before 3.1.0. There is at least one tape that is
3269 affected by this exception, which is the tape for the one-level set "Bug
3270 Machine" by Juergen Bonhagen.
3273 game.use_change_when_pushing_bug =
3274 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3276 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3277 tape.game_version < VERSION_IDENT(3,1,1,0)));
3280 Summary of bugfix/change:
3281 Fixed handling for blocking the field the player leaves when moving.
3283 Fixed/changed in version:
3287 Before 3.1.1, when "block last field when moving" was enabled, the field
3288 the player is leaving when moving was blocked for the time of the move,
3289 and was directly unblocked afterwards. This resulted in the last field
3290 being blocked for exactly one less than the number of frames of one player
3291 move. Additionally, even when blocking was disabled, the last field was
3292 blocked for exactly one frame.
3293 Since 3.1.1, due to changes in player movement handling, the last field
3294 is not blocked at all when blocking is disabled. When blocking is enabled,
3295 the last field is blocked for exactly the number of frames of one player
3296 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3297 last field is blocked for exactly one more than the number of frames of
3300 Affected levels/tapes:
3301 (!!! yet to be determined -- probably many !!!)
3304 game.use_block_last_field_bug =
3305 (game.engine_version < VERSION_IDENT(3,1,1,0));
3308 Summary of bugfix/change:
3309 Changed behaviour of CE changes with multiple changes per single frame.
3311 Fixed/changed in version:
3315 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3316 This resulted in race conditions where CEs seem to behave strange in some
3317 situations (where triggered CE changes were just skipped because there was
3318 already a CE change on that tile in the playfield in that engine frame).
3319 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3320 (The number of changes per frame must be limited in any case, because else
3321 it is easily possible to define CE changes that would result in an infinite
3322 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3323 should be set large enough so that it would only be reached in cases where
3324 the corresponding CE change conditions run into a loop. Therefore, it seems
3325 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3326 maximal number of change pages for custom elements.)
3328 Affected levels/tapes:
3332 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3333 game.max_num_changes_per_frame = 1;
3335 game.max_num_changes_per_frame =
3336 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3339 /* ---------------------------------------------------------------------- */
3341 /* default scan direction: scan playfield from top/left to bottom/right */
3342 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3344 /* dynamically adjust element properties according to game engine version */
3345 InitElementPropertiesEngine(game.engine_version);
3348 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3349 printf(" tape version == %06d [%s] [file: %06d]\n",
3350 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3352 printf(" => game.engine_version == %06d\n", game.engine_version);
3355 /* ---------- initialize player's initial move delay --------------------- */
3357 /* dynamically adjust player properties according to level information */
3358 for (i = 0; i < MAX_PLAYERS; i++)
3359 game.initial_move_delay_value[i] =
3360 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3362 /* dynamically adjust player properties according to game engine version */
3363 for (i = 0; i < MAX_PLAYERS; i++)
3364 game.initial_move_delay[i] =
3365 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3366 game.initial_move_delay_value[i] : 0);
3368 /* ---------- initialize player's initial push delay --------------------- */
3370 /* dynamically adjust player properties according to game engine version */
3371 game.initial_push_delay_value =
3372 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3374 /* ---------- initialize changing elements ------------------------------- */
3376 /* initialize changing elements information */
3377 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379 struct ElementInfo *ei = &element_info[i];
3381 /* this pointer might have been changed in the level editor */
3382 ei->change = &ei->change_page[0];
3384 if (!IS_CUSTOM_ELEMENT(i))
3386 ei->change->target_element = EL_EMPTY_SPACE;
3387 ei->change->delay_fixed = 0;
3388 ei->change->delay_random = 0;
3389 ei->change->delay_frames = 1;
3392 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3394 ei->has_change_event[j] = FALSE;
3396 ei->event_page_nr[j] = 0;
3397 ei->event_page[j] = &ei->change_page[0];
3401 /* add changing elements from pre-defined list */
3402 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3404 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3405 struct ElementInfo *ei = &element_info[ch_delay->element];
3407 ei->change->target_element = ch_delay->target_element;
3408 ei->change->delay_fixed = ch_delay->change_delay;
3410 ei->change->pre_change_function = ch_delay->pre_change_function;
3411 ei->change->change_function = ch_delay->change_function;
3412 ei->change->post_change_function = ch_delay->post_change_function;
3414 ei->change->can_change = TRUE;
3415 ei->change->can_change_or_has_action = TRUE;
3417 ei->has_change_event[CE_DELAY] = TRUE;
3419 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3420 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3423 /* ---------- initialize internal run-time variables --------------------- */
3425 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3427 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3429 for (j = 0; j < ei->num_change_pages; j++)
3431 ei->change_page[j].can_change_or_has_action =
3432 (ei->change_page[j].can_change |
3433 ei->change_page[j].has_action);
3437 /* add change events from custom element configuration */
3438 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3440 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3442 for (j = 0; j < ei->num_change_pages; j++)
3444 if (!ei->change_page[j].can_change_or_has_action)
3447 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3449 /* only add event page for the first page found with this event */
3450 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3452 ei->has_change_event[k] = TRUE;
3454 ei->event_page_nr[k] = j;
3455 ei->event_page[k] = &ei->change_page[j];
3462 /* ---------- initialize reference elements in change conditions --------- */
3464 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3466 int element = EL_CUSTOM_START + i;
3467 struct ElementInfo *ei = &element_info[element];
3469 for (j = 0; j < ei->num_change_pages; j++)
3471 int trigger_element = ei->change_page[j].initial_trigger_element;
3473 if (trigger_element >= EL_PREV_CE_8 &&
3474 trigger_element <= EL_NEXT_CE_8)
3475 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3477 ei->change_page[j].trigger_element = trigger_element;
3482 /* ---------- initialize run-time trigger player and element ------------- */
3484 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3486 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3488 for (j = 0; j < ei->num_change_pages; j++)
3490 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3491 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3492 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3493 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3494 ei->change_page[j].actual_trigger_ce_value = 0;
3495 ei->change_page[j].actual_trigger_ce_score = 0;
3499 /* ---------- initialize trigger events ---------------------------------- */
3501 /* initialize trigger events information */
3502 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3503 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3504 trigger_events[i][j] = FALSE;
3506 /* add trigger events from element change event properties */
3507 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509 struct ElementInfo *ei = &element_info[i];
3511 for (j = 0; j < ei->num_change_pages; j++)
3513 if (!ei->change_page[j].can_change_or_has_action)
3516 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3518 int trigger_element = ei->change_page[j].trigger_element;
3520 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3522 if (ei->change_page[j].has_event[k])
3524 if (IS_GROUP_ELEMENT(trigger_element))
3526 struct ElementGroupInfo *group =
3527 element_info[trigger_element].group;
3529 for (l = 0; l < group->num_elements_resolved; l++)
3530 trigger_events[group->element_resolved[l]][k] = TRUE;
3532 else if (trigger_element == EL_ANY_ELEMENT)
3533 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3534 trigger_events[l][k] = TRUE;
3536 trigger_events[trigger_element][k] = TRUE;
3543 /* ---------- initialize push delay -------------------------------------- */
3545 /* initialize push delay values to default */
3546 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3548 if (!IS_CUSTOM_ELEMENT(i))
3550 /* set default push delay values (corrected since version 3.0.7-1) */
3551 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3553 element_info[i].push_delay_fixed = 2;
3554 element_info[i].push_delay_random = 8;
3558 element_info[i].push_delay_fixed = 8;
3559 element_info[i].push_delay_random = 8;
3564 /* set push delay value for certain elements from pre-defined list */
3565 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3567 int e = push_delay_list[i].element;
3569 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3570 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3573 /* set push delay value for Supaplex elements for newer engine versions */
3574 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578 if (IS_SP_ELEMENT(i))
3580 /* set SP push delay to just enough to push under a falling zonk */
3581 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3583 element_info[i].push_delay_fixed = delay;
3584 element_info[i].push_delay_random = 0;
3589 /* ---------- initialize move stepsize ----------------------------------- */
3591 /* initialize move stepsize values to default */
3592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593 if (!IS_CUSTOM_ELEMENT(i))
3594 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3596 /* set move stepsize value for certain elements from pre-defined list */
3597 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3599 int e = move_stepsize_list[i].element;
3601 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3604 /* ---------- initialize collect score ----------------------------------- */
3606 /* initialize collect score values for custom elements from initial value */
3607 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608 if (IS_CUSTOM_ELEMENT(i))
3609 element_info[i].collect_score = element_info[i].collect_score_initial;
3611 /* ---------- initialize collect count ----------------------------------- */
3613 /* initialize collect count values for non-custom elements */
3614 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3615 if (!IS_CUSTOM_ELEMENT(i))
3616 element_info[i].collect_count_initial = 0;
3618 /* add collect count values for all elements from pre-defined list */
3619 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3620 element_info[collect_count_list[i].element].collect_count_initial =
3621 collect_count_list[i].count;
3623 /* ---------- initialize access direction -------------------------------- */
3625 /* initialize access direction values to default (access from every side) */
3626 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3627 if (!IS_CUSTOM_ELEMENT(i))
3628 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3630 /* set access direction value for certain elements from pre-defined list */
3631 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3632 element_info[access_direction_list[i].element].access_direction =
3633 access_direction_list[i].direction;
3635 /* ---------- initialize explosion content ------------------------------- */
3636 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3638 if (IS_CUSTOM_ELEMENT(i))
3641 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3643 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3645 element_info[i].content.e[x][y] =
3646 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3647 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3648 i == EL_PLAYER_3 ? EL_EMERALD :
3649 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3650 i == EL_MOLE ? EL_EMERALD_RED :
3651 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3652 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3653 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3654 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3655 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3656 i == EL_WALL_EMERALD ? EL_EMERALD :
3657 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3658 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3659 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3660 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3661 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3662 i == EL_WALL_PEARL ? EL_PEARL :
3663 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3668 /* ---------- initialize recursion detection ------------------------------ */
3669 recursion_loop_depth = 0;
3670 recursion_loop_detected = FALSE;
3671 recursion_loop_element = EL_UNDEFINED;
3673 /* ---------- initialize graphics engine ---------------------------------- */
3674 game.scroll_delay_value =
3675 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3676 setup.scroll_delay ? setup.scroll_delay_value : 0);
3677 game.scroll_delay_value =
3678 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3681 int get_num_special_action(int element, int action_first, int action_last)
3683 int num_special_action = 0;
3686 for (i = action_first; i <= action_last; i++)
3688 boolean found = FALSE;
3690 for (j = 0; j < NUM_DIRECTIONS; j++)
3691 if (el_act_dir2img(element, i, j) !=
3692 el_act_dir2img(element, ACTION_DEFAULT, j))
3696 num_special_action++;
3701 return num_special_action;
3706 =============================================================================
3708 -----------------------------------------------------------------------------
3709 initialize and start new game
3710 =============================================================================
3715 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3716 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3717 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3719 boolean do_fading = (game_status == GAME_MODE_MAIN);
3722 int initial_move_dir = MV_DOWN;
3724 int initial_move_dir = MV_NONE;
3728 game_status = GAME_MODE_PLAYING;
3731 /* needed if different viewport properties defined for playing */
3732 ChangeViewportPropertiesIfNeeded();
3736 DrawCompleteVideoDisplay();
3740 InitGameControlValues();
3742 /* don't play tapes over network */
3743 network_playing = (options.network && !tape.playing);
3745 for (i = 0; i < MAX_PLAYERS; i++)
3747 struct PlayerInfo *player = &stored_player[i];
3749 player->index_nr = i;
3750 player->index_bit = (1 << i);
3751 player->element_nr = EL_PLAYER_1 + i;
3753 player->present = FALSE;
3754 player->active = FALSE;
3755 player->mapped = FALSE;
3757 player->killed = FALSE;
3758 player->reanimated = FALSE;
3761 player->effective_action = 0;
3762 player->programmed_action = 0;
3765 player->score_final = 0;
3767 player->gems_still_needed = level.gems_needed;
3768 player->sokobanfields_still_needed = 0;
3769 player->lights_still_needed = 0;
3770 player->friends_still_needed = 0;
3772 for (j = 0; j < MAX_NUM_KEYS; j++)
3773 player->key[j] = FALSE;
3775 player->num_white_keys = 0;
3777 player->dynabomb_count = 0;
3778 player->dynabomb_size = 1;
3779 player->dynabombs_left = 0;
3780 player->dynabomb_xl = FALSE;
3782 player->MovDir = initial_move_dir;
3785 player->GfxDir = initial_move_dir;
3786 player->GfxAction = ACTION_DEFAULT;
3788 player->StepFrame = 0;
3790 player->initial_element = player->element_nr;
3791 player->artwork_element =
3792 (level.use_artwork_element[i] ? level.artwork_element[i] :
3793 player->element_nr);
3794 player->use_murphy = FALSE;
3796 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3797 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3799 player->gravity = level.initial_player_gravity[i];
3801 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3803 player->actual_frame_counter = 0;
3805 player->step_counter = 0;
3807 player->last_move_dir = initial_move_dir;
3809 player->is_active = FALSE;
3811 player->is_waiting = FALSE;
3812 player->is_moving = FALSE;
3813 player->is_auto_moving = FALSE;
3814 player->is_digging = FALSE;
3815 player->is_snapping = FALSE;
3816 player->is_collecting = FALSE;
3817 player->is_pushing = FALSE;
3818 player->is_switching = FALSE;
3819 player->is_dropping = FALSE;
3820 player->is_dropping_pressed = FALSE;
3822 player->is_bored = FALSE;
3823 player->is_sleeping = FALSE;
3825 player->frame_counter_bored = -1;
3826 player->frame_counter_sleeping = -1;
3828 player->anim_delay_counter = 0;
3829 player->post_delay_counter = 0;
3831 player->dir_waiting = initial_move_dir;
3832 player->action_waiting = ACTION_DEFAULT;
3833 player->last_action_waiting = ACTION_DEFAULT;
3834 player->special_action_bored = ACTION_DEFAULT;
3835 player->special_action_sleeping = ACTION_DEFAULT;
3837 player->switch_x = -1;
3838 player->switch_y = -1;
3840 player->drop_x = -1;
3841 player->drop_y = -1;
3843 player->show_envelope = 0;
3845 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3847 player->push_delay = -1; /* initialized when pushing starts */
3848 player->push_delay_value = game.initial_push_delay_value;
3850 player->drop_delay = 0;
3851 player->drop_pressed_delay = 0;
3853 player->last_jx = -1;
3854 player->last_jy = -1;
3858 player->shield_normal_time_left = 0;
3859 player->shield_deadly_time_left = 0;
3861 player->inventory_infinite_element = EL_UNDEFINED;
3862 player->inventory_size = 0;
3864 if (level.use_initial_inventory[i])
3866 for (j = 0; j < level.initial_inventory_size[i]; j++)
3868 int element = level.initial_inventory_content[i][j];
3869 int collect_count = element_info[element].collect_count_initial;
3872 if (!IS_CUSTOM_ELEMENT(element))
3875 if (collect_count == 0)
3876 player->inventory_infinite_element = element;
3878 for (k = 0; k < collect_count; k++)
3879 if (player->inventory_size < MAX_INVENTORY_SIZE)
3880 player->inventory_element[player->inventory_size++] = element;
3884 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3885 SnapField(player, 0, 0);
3887 player->LevelSolved = FALSE;
3888 player->GameOver = FALSE;
3890 player->LevelSolved_GameWon = FALSE;
3891 player->LevelSolved_GameEnd = FALSE;
3892 player->LevelSolved_PanelOff = FALSE;
3893 player->LevelSolved_SaveTape = FALSE;
3894 player->LevelSolved_SaveScore = FALSE;
3895 player->LevelSolved_CountingTime = 0;
3896 player->LevelSolved_CountingScore = 0;
3898 map_player_action[i] = i;
3901 network_player_action_received = FALSE;
3903 #if defined(NETWORK_AVALIABLE)
3904 /* initial null action */
3905 if (network_playing)
3906 SendToServer_MovePlayer(MV_NONE);
3915 TimeLeft = level.time;
3918 ScreenMovDir = MV_NONE;
3922 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3924 AllPlayersGone = FALSE;
3926 game.yamyam_content_nr = 0;
3927 game.robot_wheel_active = FALSE;
3928 game.magic_wall_active = FALSE;
3929 game.magic_wall_time_left = 0;
3930 game.light_time_left = 0;
3931 game.timegate_time_left = 0;
3932 game.switchgate_pos = 0;
3933 game.wind_direction = level.wind_direction_initial;
3935 #if !USE_PLAYER_GRAVITY
3936 game.gravity = FALSE;
3937 game.explosions_delayed = TRUE;
3940 game.lenses_time_left = 0;
3941 game.magnify_time_left = 0;
3943 game.ball_state = level.ball_state_initial;
3944 game.ball_content_nr = 0;
3946 game.envelope_active = FALSE;
3948 /* set focus to local player for network games, else to all players */
3949 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3950 game.centered_player_nr_next = game.centered_player_nr;
3951 game.set_centered_player = FALSE;
3953 if (network_playing && tape.recording)
3955 /* store client dependent player focus when recording network games */
3956 tape.centered_player_nr_next = game.centered_player_nr_next;
3957 tape.set_centered_player = TRUE;
3960 for (i = 0; i < NUM_BELTS; i++)
3962 game.belt_dir[i] = MV_NONE;
3963 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3966 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3967 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3969 SCAN_PLAYFIELD(x, y)
3971 Feld[x][y] = level.field[x][y];
3972 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3973 ChangeDelay[x][y] = 0;
3974 ChangePage[x][y] = -1;
3975 #if USE_NEW_CUSTOM_VALUE
3976 CustomValue[x][y] = 0; /* initialized in InitField() */
3978 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3980 WasJustMoving[x][y] = 0;
3981 WasJustFalling[x][y] = 0;
3982 CheckCollision[x][y] = 0;
3983 CheckImpact[x][y] = 0;
3985 Pushed[x][y] = FALSE;
3987 ChangeCount[x][y] = 0;
3988 ChangeEvent[x][y] = -1;
3990 ExplodePhase[x][y] = 0;
3991 ExplodeDelay[x][y] = 0;
3992 ExplodeField[x][y] = EX_TYPE_NONE;
3994 RunnerVisit[x][y] = 0;
3995 PlayerVisit[x][y] = 0;
3998 GfxRandom[x][y] = INIT_GFX_RANDOM();
3999 GfxElement[x][y] = EL_UNDEFINED;
4000 GfxAction[x][y] = ACTION_DEFAULT;
4001 GfxDir[x][y] = MV_NONE;
4002 GfxRedraw[x][y] = GFX_REDRAW_NONE;
4005 SCAN_PLAYFIELD(x, y)
4007 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4009 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4011 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4014 InitField(x, y, TRUE);
4016 ResetGfxAnimation(x, y);
4021 for (i = 0; i < MAX_PLAYERS; i++)
4023 struct PlayerInfo *player = &stored_player[i];
4025 /* set number of special actions for bored and sleeping animation */
4026 player->num_special_action_bored =
4027 get_num_special_action(player->artwork_element,
4028 ACTION_BORING_1, ACTION_BORING_LAST);
4029 player->num_special_action_sleeping =
4030 get_num_special_action(player->artwork_element,
4031 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4034 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4035 emulate_sb ? EMU_SOKOBAN :
4036 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4038 #if USE_NEW_ALL_SLIPPERY
4039 /* initialize type of slippery elements */
4040 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4042 if (!IS_CUSTOM_ELEMENT(i))
4044 /* default: elements slip down either to the left or right randomly */
4045 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4047 /* SP style elements prefer to slip down on the left side */
4048 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4049 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4051 /* BD style elements prefer to slip down on the left side */
4052 if (game.emulation == EMU_BOULDERDASH)
4053 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4058 /* initialize explosion and ignition delay */
4059 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4061 if (!IS_CUSTOM_ELEMENT(i))
4064 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4065 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4066 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4067 int last_phase = (num_phase + 1) * delay;
4068 int half_phase = (num_phase / 2) * delay;
4070 element_info[i].explosion_delay = last_phase - 1;
4071 element_info[i].ignition_delay = half_phase;
4073 if (i == EL_BLACK_ORB)
4074 element_info[i].ignition_delay = 1;
4078 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4079 element_info[i].explosion_delay = 1;
4081 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4082 element_info[i].ignition_delay = 1;
4086 /* correct non-moving belts to start moving left */
4087 for (i = 0; i < NUM_BELTS; i++)
4088 if (game.belt_dir[i] == MV_NONE)
4089 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4091 #if USE_NEW_PLAYER_ASSIGNMENTS
4092 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4093 /* choose default local player */
4094 local_player = &stored_player[0];
4096 for (i = 0; i < MAX_PLAYERS; i++)
4097 stored_player[i].connected = FALSE;
4099 local_player->connected = TRUE;
4100 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4104 /* try to guess locally connected team mode players (needed for correct
4105 assignment of player figures from level to locally playing players) */
4107 for (i = 0; i < MAX_PLAYERS; i++)
4108 if (tape.player_participates[i])
4109 stored_player[i].connected = TRUE;
4111 else if (setup.team_mode && !options.network)
4113 /* try to guess locally connected team mode players (needed for correct
4114 assignment of player figures from level to locally playing players) */
4116 for (i = 0; i < MAX_PLAYERS; i++)
4117 if (setup.input[i].use_joystick ||
4118 setup.input[i].key.left != KSYM_UNDEFINED)
4119 stored_player[i].connected = TRUE;
4123 for (i = 0; i < MAX_PLAYERS; i++)
4124 printf("::: player %d: %s\n", i,
4125 (stored_player[i].connected ? "connected" : "not connected"));
4127 for (i = 0; i < MAX_PLAYERS; i++)
4128 printf("::: player %d: %s\n", i,
4129 (stored_player[i].present ? "present" : "not present"));
4132 /* check if any connected player was not found in playfield */
4133 for (i = 0; i < MAX_PLAYERS; i++)
4135 struct PlayerInfo *player = &stored_player[i];
4137 if (player->connected && !player->present)
4139 struct PlayerInfo *field_player = NULL;
4142 printf("::: looking for field player for player %d ...\n", i);
4145 /* assign first free player found that is present in the playfield */
4147 /* first try: look for unmapped playfield player that is not connected */
4148 if (field_player == NULL)
4149 for (j = 0; j < MAX_PLAYERS; j++)
4150 if (stored_player[j].present &&
4151 !stored_player[j].mapped &&
4152 !stored_player[j].connected)
4153 field_player = &stored_player[j];
4155 /* second try: look for *any* unmapped playfield player */
4156 if (field_player == NULL)
4157 for (j = 0; j < MAX_PLAYERS; j++)
4158 if (stored_player[j].present &&
4159 !stored_player[j].mapped)
4160 field_player = &stored_player[j];
4162 if (field_player != NULL)
4164 int jx = field_player->jx, jy = field_player->jy;
4167 printf("::: found player figure %d\n", field_player->index_nr);
4170 player->present = FALSE;
4171 player->active = FALSE;
4173 field_player->present = TRUE;
4174 field_player->active = TRUE;
4177 player->initial_element = field_player->initial_element;
4178 player->artwork_element = field_player->artwork_element;
4180 player->block_last_field = field_player->block_last_field;
4181 player->block_delay_adjustment = field_player->block_delay_adjustment;
4184 StorePlayer[jx][jy] = field_player->element_nr;
4186 field_player->jx = field_player->last_jx = jx;
4187 field_player->jy = field_player->last_jy = jy;
4189 if (local_player == player)
4190 local_player = field_player;
4192 map_player_action[field_player->index_nr] = i;
4194 field_player->mapped = TRUE;
4197 printf("::: map_player_action[%d] == %d\n",
4198 field_player->index_nr, i);
4203 if (player->connected && player->present)
4204 player->mapped = TRUE;
4209 /* check if any connected player was not found in playfield */
4210 for (i = 0; i < MAX_PLAYERS; i++)
4212 struct PlayerInfo *player = &stored_player[i];
4214 if (player->connected && !player->present)
4216 for (j = 0; j < MAX_PLAYERS; j++)
4218 struct PlayerInfo *field_player = &stored_player[j];
4219 int jx = field_player->jx, jy = field_player->jy;
4221 /* assign first free player found that is present in the playfield */
4222 if (field_player->present && !field_player->connected)
4224 player->present = TRUE;
4225 player->active = TRUE;
4227 field_player->present = FALSE;
4228 field_player->active = FALSE;
4230 player->initial_element = field_player->initial_element;
4231 player->artwork_element = field_player->artwork_element;
4233 player->block_last_field = field_player->block_last_field;
4234 player->block_delay_adjustment = field_player->block_delay_adjustment;
4236 StorePlayer[jx][jy] = player->element_nr;
4238 player->jx = player->last_jx = jx;
4239 player->jy = player->last_jy = jy;
4249 printf("::: local_player->present == %d\n", local_player->present);
4254 /* when playing a tape, eliminate all players who do not participate */
4256 #if USE_NEW_PLAYER_ASSIGNMENTS
4257 for (i = 0; i < MAX_PLAYERS; i++)
4259 if (stored_player[i].active &&
4260 !tape.player_participates[map_player_action[i]])
4262 struct PlayerInfo *player = &stored_player[i];
4263 int jx = player->jx, jy = player->jy;
4265 player->active = FALSE;
4266 StorePlayer[jx][jy] = 0;
4267 Feld[jx][jy] = EL_EMPTY;
4271 for (i = 0; i < MAX_PLAYERS; i++)
4273 if (stored_player[i].active &&
4274 !tape.player_participates[i])
4276 struct PlayerInfo *player = &stored_player[i];
4277 int jx = player->jx, jy = player->jy;
4279 player->active = FALSE;
4280 StorePlayer[jx][jy] = 0;
4281 Feld[jx][jy] = EL_EMPTY;
4286 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4288 /* when in single player mode, eliminate all but the first active player */
4290 for (i = 0; i < MAX_PLAYERS; i++)
4292 if (stored_player[i].active)
4294 for (j = i + 1; j < MAX_PLAYERS; j++)
4296 if (stored_player[j].active)
4298 struct PlayerInfo *player = &stored_player[j];
4299 int jx = player->jx, jy = player->jy;
4301 player->active = FALSE;
4302 player->present = FALSE;
4304 StorePlayer[jx][jy] = 0;
4305 Feld[jx][jy] = EL_EMPTY;
4312 /* when recording the game, store which players take part in the game */
4315 #if USE_NEW_PLAYER_ASSIGNMENTS
4316 for (i = 0; i < MAX_PLAYERS; i++)
4317 if (stored_player[i].connected)
4318 tape.player_participates[i] = TRUE;
4320 for (i = 0; i < MAX_PLAYERS; i++)
4321 if (stored_player[i].active)
4322 tape.player_participates[i] = TRUE;
4328 for (i = 0; i < MAX_PLAYERS; i++)
4330 struct PlayerInfo *player = &stored_player[i];
4332 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4337 if (local_player == player)
4338 printf("Player %d is local player.\n", i+1);
4342 if (BorderElement == EL_EMPTY)
4345 SBX_Right = lev_fieldx - SCR_FIELDX;
4347 SBY_Lower = lev_fieldy - SCR_FIELDY;
4352 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4354 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4359 // if (TILESIZE_VAR < TILESIZE && EVEN(SCR_FIELDX))
4360 if (EVEN(SCR_FIELDX))
4368 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4369 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4371 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4372 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4374 /* if local player not found, look for custom element that might create
4375 the player (make some assumptions about the right custom element) */
4376 if (!local_player->present)
4378 int start_x = 0, start_y = 0;
4379 int found_rating = 0;
4380 int found_element = EL_UNDEFINED;
4381 int player_nr = local_player->index_nr;
4383 SCAN_PLAYFIELD(x, y)
4385 int element = Feld[x][y];
4390 if (level.use_start_element[player_nr] &&
4391 level.start_element[player_nr] == element &&
4398 found_element = element;
4401 if (!IS_CUSTOM_ELEMENT(element))
4404 if (CAN_CHANGE(element))
4406 for (i = 0; i < element_info[element].num_change_pages; i++)
4408 /* check for player created from custom element as single target */
4409 content = element_info[element].change_page[i].target_element;
4410 is_player = ELEM_IS_PLAYER(content);
4412 if (is_player && (found_rating < 3 ||
4413 (found_rating == 3 && element < found_element)))
4419 found_element = element;
4424 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4426 /* check for player created from custom element as explosion content */
4427 content = element_info[element].content.e[xx][yy];
4428 is_player = ELEM_IS_PLAYER(content);
4430 if (is_player && (found_rating < 2 ||
4431 (found_rating == 2 && element < found_element)))
4433 start_x = x + xx - 1;
4434 start_y = y + yy - 1;
4437 found_element = element;
4440 if (!CAN_CHANGE(element))
4443 for (i = 0; i < element_info[element].num_change_pages; i++)
4445 /* check for player created from custom element as extended target */
4447 element_info[element].change_page[i].target_content.e[xx][yy];
4449 is_player = ELEM_IS_PLAYER(content);
4451 if (is_player && (found_rating < 1 ||
4452 (found_rating == 1 && element < found_element)))
4454 start_x = x + xx - 1;
4455 start_y = y + yy - 1;
4458 found_element = element;
4464 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4465 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4468 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4469 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4474 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4475 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4476 local_player->jx - MIDPOSX);
4478 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4479 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4480 local_player->jy - MIDPOSY);
4484 /* do not use PLAYING mask for fading out from main screen */
4485 game_status = GAME_MODE_MAIN;
4490 if (!game.restart_level)
4491 CloseDoor(DOOR_CLOSE_1);
4494 if (level_editor_test_game)
4495 FadeSkipNextFadeIn();
4497 FadeSetEnterScreen();
4499 if (level_editor_test_game)
4500 fading = fading_none;
4502 fading = menu.destination;
4506 FadeOut(REDRAW_FIELD);
4509 FadeOut(REDRAW_FIELD);
4513 game_status = GAME_MODE_PLAYING;
4516 /* !!! FIX THIS (START) !!! */
4517 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4519 InitGameEngine_EM();
4521 /* blit playfield from scroll buffer to normal back buffer for fading in */
4522 BlitScreenToBitmap_EM(backbuffer);
4524 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4526 InitGameEngine_SP();
4528 /* blit playfield from scroll buffer to normal back buffer for fading in */
4529 BlitScreenToBitmap_SP(backbuffer);
4536 /* after drawing the level, correct some elements */
4537 if (game.timegate_time_left == 0)
4538 CloseAllOpenTimegates();
4541 BlitScreenToBitmap(backbuffer);
4543 /* blit playfield from scroll buffer to normal back buffer for fading in */
4544 if (setup.soft_scrolling)
4545 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4548 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4550 /* !!! FIX THIS (END) !!! */
4553 FadeIn(REDRAW_FIELD);
4556 FadeIn(REDRAW_FIELD);
4561 if (!game.restart_level)
4563 /* copy default game door content to main double buffer */
4566 /* !!! CHECK AGAIN !!! */
4567 SetPanelBackground();
4568 // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4569 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4571 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4573 /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4574 ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4575 BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4576 MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4579 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4580 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4584 SetPanelBackground();
4585 SetDrawBackgroundMask(REDRAW_DOOR_1);
4588 UpdateAndDisplayGameControlValues();
4590 UpdateGameDoorValues();
4591 DrawGameDoorValues();
4594 if (!game.restart_level)
4598 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4599 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4600 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4604 /* copy actual game door content to door double buffer for OpenDoor() */
4605 BlitBitmap(drawto, bitmap_db_door,
4606 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4608 OpenDoor(DOOR_OPEN_ALL);
4610 PlaySound(SND_GAME_STARTING);
4612 if (setup.sound_music)
4615 KeyboardAutoRepeatOffUnlessAutoplay();
4619 for (i = 0; i < MAX_PLAYERS; i++)
4620 printf("Player %d %sactive.\n",
4621 i + 1, (stored_player[i].active ? "" : "not "));
4632 game.restart_level = FALSE;
4635 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4637 /* this is used for non-R'n'D game engines to update certain engine values */
4639 /* needed to determine if sounds are played within the visible screen area */
4640 scroll_x = actual_scroll_x;
4641 scroll_y = actual_scroll_y;
4644 void InitMovDir(int x, int y)
4646 int i, element = Feld[x][y];
4647 static int xy[4][2] =
4654 static int direction[3][4] =
4656 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4657 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4658 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4667 Feld[x][y] = EL_BUG;
4668 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4671 case EL_SPACESHIP_RIGHT:
4672 case EL_SPACESHIP_UP:
4673 case EL_SPACESHIP_LEFT:
4674 case EL_SPACESHIP_DOWN:
4675 Feld[x][y] = EL_SPACESHIP;
4676 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4679 case EL_BD_BUTTERFLY_RIGHT:
4680 case EL_BD_BUTTERFLY_UP:
4681 case EL_BD_BUTTERFLY_LEFT:
4682 case EL_BD_BUTTERFLY_DOWN:
4683 Feld[x][y] = EL_BD_BUTTERFLY;
4684 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4687 case EL_BD_FIREFLY_RIGHT:
4688 case EL_BD_FIREFLY_UP:
4689 case EL_BD_FIREFLY_LEFT:
4690 case EL_BD_FIREFLY_DOWN:
4691 Feld[x][y] = EL_BD_FIREFLY;
4692 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4695 case EL_PACMAN_RIGHT:
4697 case EL_PACMAN_LEFT:
4698 case EL_PACMAN_DOWN:
4699 Feld[x][y] = EL_PACMAN;
4700 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4703 case EL_YAMYAM_LEFT:
4704 case EL_YAMYAM_RIGHT:
4706 case EL_YAMYAM_DOWN:
4707 Feld[x][y] = EL_YAMYAM;
4708 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4711 case EL_SP_SNIKSNAK:
4712 MovDir[x][y] = MV_UP;
4715 case EL_SP_ELECTRON:
4716 MovDir[x][y] = MV_LEFT;
4723 Feld[x][y] = EL_MOLE;
4724 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4728 if (IS_CUSTOM_ELEMENT(element))
4730 struct ElementInfo *ei = &element_info[element];
4731 int move_direction_initial = ei->move_direction_initial;
4732 int move_pattern = ei->move_pattern;
4734 if (move_direction_initial == MV_START_PREVIOUS)
4736 if (MovDir[x][y] != MV_NONE)
4739 move_direction_initial = MV_START_AUTOMATIC;
4742 if (move_direction_initial == MV_START_RANDOM)
4743 MovDir[x][y] = 1 << RND(4);
4744 else if (move_direction_initial & MV_ANY_DIRECTION)
4745 MovDir[x][y] = move_direction_initial;
4746 else if (move_pattern == MV_ALL_DIRECTIONS ||
4747 move_pattern == MV_TURNING_LEFT ||
4748 move_pattern == MV_TURNING_RIGHT ||
4749 move_pattern == MV_TURNING_LEFT_RIGHT ||
4750 move_pattern == MV_TURNING_RIGHT_LEFT ||
4751 move_pattern == MV_TURNING_RANDOM)
4752 MovDir[x][y] = 1 << RND(4);
4753 else if (move_pattern == MV_HORIZONTAL)
4754 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4755 else if (move_pattern == MV_VERTICAL)
4756 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4757 else if (move_pattern & MV_ANY_DIRECTION)
4758 MovDir[x][y] = element_info[element].move_pattern;
4759 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4760 move_pattern == MV_ALONG_RIGHT_SIDE)
4762 /* use random direction as default start direction */
4763 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4764 MovDir[x][y] = 1 << RND(4);
4766 for (i = 0; i < NUM_DIRECTIONS; i++)
4768 int x1 = x + xy[i][0];
4769 int y1 = y + xy[i][1];
4771 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4773 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4774 MovDir[x][y] = direction[0][i];
4776 MovDir[x][y] = direction[1][i];
4785 MovDir[x][y] = 1 << RND(4);
4787 if (element != EL_BUG &&
4788 element != EL_SPACESHIP &&
4789 element != EL_BD_BUTTERFLY &&
4790 element != EL_BD_FIREFLY)
4793 for (i = 0; i < NUM_DIRECTIONS; i++)
4795 int x1 = x + xy[i][0];
4796 int y1 = y + xy[i][1];
4798 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4800 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4802 MovDir[x][y] = direction[0][i];
4805 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4806 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4808 MovDir[x][y] = direction[1][i];
4817 GfxDir[x][y] = MovDir[x][y];
4820 void InitAmoebaNr(int x, int y)
4823 int group_nr = AmoebeNachbarNr(x, y);
4827 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4829 if (AmoebaCnt[i] == 0)
4837 AmoebaNr[x][y] = group_nr;
4838 AmoebaCnt[group_nr]++;
4839 AmoebaCnt2[group_nr]++;
4842 static void PlayerWins(struct PlayerInfo *player)
4844 player->LevelSolved = TRUE;
4845 player->GameOver = TRUE;
4847 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4848 level.native_em_level->lev->score : player->score);
4850 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4851 player->LevelSolved_CountingScore = player->score_final;
4856 static int time, time_final;
4857 static int score, score_final;
4858 static int game_over_delay_1 = 0;
4859 static int game_over_delay_2 = 0;
4860 int game_over_delay_value_1 = 50;
4861 int game_over_delay_value_2 = 50;
4863 if (!local_player->LevelSolved_GameWon)
4867 /* do not start end game actions before the player stops moving (to exit) */
4868 if (local_player->MovPos)
4871 local_player->LevelSolved_GameWon = TRUE;
4872 local_player->LevelSolved_SaveTape = tape.recording;
4873 local_player->LevelSolved_SaveScore = !tape.playing;
4875 if (tape.auto_play) /* tape might already be stopped here */
4876 tape.auto_play_level_solved = TRUE;
4882 game_over_delay_1 = game_over_delay_value_1;
4883 game_over_delay_2 = game_over_delay_value_2;
4885 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4886 score = score_final = local_player->score_final;
4891 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4893 else if (level.time == 0 && TimePlayed < 999)
4896 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4899 local_player->score_final = score_final;
4901 if (level_editor_test_game)
4904 score = score_final;
4907 local_player->LevelSolved_CountingTime = time;
4908 local_player->LevelSolved_CountingScore = score;
4910 game_panel_controls[GAME_PANEL_TIME].value = time;
4911 game_panel_controls[GAME_PANEL_SCORE].value = score;
4913 DisplayGameControlValues();
4915 DrawGameValue_Time(time);
4916 DrawGameValue_Score(score);
4920 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4922 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4924 /* close exit door after last player */
4925 if ((AllPlayersGone &&
4926 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4927 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4928 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4929 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4930 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4932 int element = Feld[ExitX][ExitY];
4935 if (element == EL_EM_EXIT_OPEN ||
4936 element == EL_EM_STEEL_EXIT_OPEN)
4943 Feld[ExitX][ExitY] =
4944 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4945 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4946 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4947 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4948 EL_EM_STEEL_EXIT_CLOSING);
4950 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4954 /* player disappears */
4955 DrawLevelField(ExitX, ExitY);
4958 for (i = 0; i < MAX_PLAYERS; i++)
4960 struct PlayerInfo *player = &stored_player[i];
4962 if (player->present)
4964 RemovePlayer(player);
4966 /* player disappears */
4967 DrawLevelField(player->jx, player->jy);
4972 PlaySound(SND_GAME_WINNING);
4975 if (game_over_delay_1 > 0)
4977 game_over_delay_1--;
4982 if (time != time_final)
4984 int time_to_go = ABS(time_final - time);
4985 int time_count_dir = (time < time_final ? +1 : -1);
4986 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4988 time += time_count_steps * time_count_dir;
4989 score += time_count_steps * level.score[SC_TIME_BONUS];
4992 local_player->LevelSolved_CountingTime = time;
4993 local_player->LevelSolved_CountingScore = score;
4995 game_panel_controls[GAME_PANEL_TIME].value = time;
4996 game_panel_controls[GAME_PANEL_SCORE].value = score;
4998 DisplayGameControlValues();
5000 DrawGameValue_Time(time);
5001 DrawGameValue_Score(score);
5004 if (time == time_final)
5005 StopSound(SND_GAME_LEVELTIME_BONUS);
5006 else if (setup.sound_loops)
5007 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5009 PlaySound(SND_GAME_LEVELTIME_BONUS);
5014 local_player->LevelSolved_PanelOff = TRUE;
5016 if (game_over_delay_2 > 0)
5018 game_over_delay_2--;
5031 boolean raise_level = FALSE;
5033 local_player->LevelSolved_GameEnd = TRUE;
5035 CloseDoor(DOOR_CLOSE_1);
5037 if (local_player->LevelSolved_SaveTape)
5044 SaveTapeChecked(tape.level_nr); /* ask to save tape */
5046 SaveTape(tape.level_nr); /* ask to save tape */
5050 if (level_editor_test_game)
5052 game_status = GAME_MODE_MAIN;
5055 DrawAndFadeInMainMenu(REDRAW_FIELD);
5063 if (!local_player->LevelSolved_SaveScore)
5066 FadeOut(REDRAW_FIELD);
5069 game_status = GAME_MODE_MAIN;
5071 DrawAndFadeInMainMenu(REDRAW_FIELD);
5076 if (level_nr == leveldir_current->handicap_level)
5078 leveldir_current->handicap_level++;
5079 SaveLevelSetup_SeriesInfo();
5082 if (level_nr < leveldir_current->last_level)
5083 raise_level = TRUE; /* advance to next level */
5085 if ((hi_pos = NewHiScore()) >= 0)
5087 game_status = GAME_MODE_SCORES;
5089 DrawHallOfFame(hi_pos);
5100 FadeOut(REDRAW_FIELD);
5103 game_status = GAME_MODE_MAIN;
5111 DrawAndFadeInMainMenu(REDRAW_FIELD);
5120 LoadScore(level_nr);
5122 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5123 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5126 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5128 if (local_player->score_final > highscore[k].Score)
5130 /* player has made it to the hall of fame */
5132 if (k < MAX_SCORE_ENTRIES - 1)
5134 int m = MAX_SCORE_ENTRIES - 1;
5137 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5138 if (strEqual(setup.player_name, highscore[l].Name))
5140 if (m == k) /* player's new highscore overwrites his old one */
5144 for (l = m; l > k; l--)
5146 strcpy(highscore[l].Name, highscore[l - 1].Name);
5147 highscore[l].Score = highscore[l - 1].Score;
5154 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5155 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5156 highscore[k].Score = local_player->score_final;
5162 else if (!strncmp(setup.player_name, highscore[k].Name,
5163 MAX_PLAYER_NAME_LEN))
5164 break; /* player already there with a higher score */
5170 SaveScore(level_nr);
5175 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5177 int element = Feld[x][y];
5178 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5179 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5180 int horiz_move = (dx != 0);
5181 int sign = (horiz_move ? dx : dy);
5182 int step = sign * element_info[element].move_stepsize;
5184 /* special values for move stepsize for spring and things on conveyor belt */
5187 if (CAN_FALL(element) &&
5188 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5189 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5190 else if (element == EL_SPRING)
5191 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5197 inline static int getElementMoveStepsize(int x, int y)
5199 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5202 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5204 if (player->GfxAction != action || player->GfxDir != dir)
5207 printf("Player frame reset! (%d => %d, %d => %d)\n",
5208 player->GfxAction, action, player->GfxDir, dir);
5211 player->GfxAction = action;
5212 player->GfxDir = dir;
5214 player->StepFrame = 0;
5218 #if USE_GFX_RESET_GFX_ANIMATION
5219 static void ResetGfxFrame(int x, int y, boolean redraw)
5221 int element = Feld[x][y];
5222 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5223 int last_gfx_frame = GfxFrame[x][y];
5225 if (graphic_info[graphic].anim_global_sync)
5226 GfxFrame[x][y] = FrameCounter;
5227 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5228 GfxFrame[x][y] = CustomValue[x][y];
5229 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5230 GfxFrame[x][y] = element_info[element].collect_score;
5231 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5232 GfxFrame[x][y] = ChangeDelay[x][y];
5234 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5235 DrawLevelGraphicAnimation(x, y, graphic);
5239 static void ResetGfxAnimation(int x, int y)
5241 GfxAction[x][y] = ACTION_DEFAULT;
5242 GfxDir[x][y] = MovDir[x][y];
5245 #if USE_GFX_RESET_GFX_ANIMATION
5246 ResetGfxFrame(x, y, FALSE);
5250 static void ResetRandomAnimationValue(int x, int y)
5252 GfxRandom[x][y] = INIT_GFX_RANDOM();
5255 void InitMovingField(int x, int y, int direction)
5257 int element = Feld[x][y];
5258 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5259 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5262 boolean is_moving_before, is_moving_after;
5264 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5267 /* check if element was/is moving or being moved before/after mode change */
5270 is_moving_before = (WasJustMoving[x][y] != 0);
5272 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5273 is_moving_before = WasJustMoving[x][y];
5276 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5278 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5280 /* reset animation only for moving elements which change direction of moving
5281 or which just started or stopped moving
5282 (else CEs with property "can move" / "not moving" are reset each frame) */
5283 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5285 if (is_moving_before != is_moving_after ||
5286 direction != MovDir[x][y])
5287 ResetGfxAnimation(x, y);
5289 if ((is_moving_before || is_moving_after) && !continues_moving)
5290 ResetGfxAnimation(x, y);
5293 if (!continues_moving)
5294 ResetGfxAnimation(x, y);
5297 MovDir[x][y] = direction;
5298 GfxDir[x][y] = direction;
5300 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5301 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5302 direction == MV_DOWN && CAN_FALL(element) ?
5303 ACTION_FALLING : ACTION_MOVING);
5305 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5306 ACTION_FALLING : ACTION_MOVING);
5309 /* this is needed for CEs with property "can move" / "not moving" */
5311 if (is_moving_after)
5313 if (Feld[newx][newy] == EL_EMPTY)
5314 Feld[newx][newy] = EL_BLOCKED;
5316 MovDir[newx][newy] = MovDir[x][y];
5318 #if USE_NEW_CUSTOM_VALUE
5319 CustomValue[newx][newy] = CustomValue[x][y];
5322 GfxFrame[newx][newy] = GfxFrame[x][y];
5323 GfxRandom[newx][newy] = GfxRandom[x][y];
5324 GfxAction[newx][newy] = GfxAction[x][y];
5325 GfxDir[newx][newy] = GfxDir[x][y];
5329 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5331 int direction = MovDir[x][y];
5332 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5333 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5339 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5341 int oldx = x, oldy = y;
5342 int direction = MovDir[x][y];
5344 if (direction == MV_LEFT)
5346 else if (direction == MV_RIGHT)
5348 else if (direction == MV_UP)
5350 else if (direction == MV_DOWN)
5353 *comes_from_x = oldx;
5354 *comes_from_y = oldy;
5357 int MovingOrBlocked2Element(int x, int y)
5359 int element = Feld[x][y];
5361 if (element == EL_BLOCKED)
5365 Blocked2Moving(x, y, &oldx, &oldy);
5366 return Feld[oldx][oldy];
5372 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5374 /* like MovingOrBlocked2Element(), but if element is moving
5375 and (x,y) is the field the moving element is just leaving,
5376 return EL_BLOCKED instead of the element value */
5377 int element = Feld[x][y];
5379 if (IS_MOVING(x, y))
5381 if (element == EL_BLOCKED)
5385 Blocked2Moving(x, y, &oldx, &oldy);
5386 return Feld[oldx][oldy];
5395 static void RemoveField(int x, int y)
5397 Feld[x][y] = EL_EMPTY;
5403 #if USE_NEW_CUSTOM_VALUE
5404 CustomValue[x][y] = 0;
5408 ChangeDelay[x][y] = 0;
5409 ChangePage[x][y] = -1;
5410 Pushed[x][y] = FALSE;
5413 ExplodeField[x][y] = EX_TYPE_NONE;
5416 GfxElement[x][y] = EL_UNDEFINED;
5417 GfxAction[x][y] = ACTION_DEFAULT;
5418 GfxDir[x][y] = MV_NONE;
5420 /* !!! this would prevent the removed tile from being redrawn !!! */
5421 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5425 void RemoveMovingField(int x, int y)
5427 int oldx = x, oldy = y, newx = x, newy = y;
5428 int element = Feld[x][y];
5429 int next_element = EL_UNDEFINED;
5431 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5434 if (IS_MOVING(x, y))
5436 Moving2Blocked(x, y, &newx, &newy);
5438 if (Feld[newx][newy] != EL_BLOCKED)
5440 /* element is moving, but target field is not free (blocked), but
5441 already occupied by something different (example: acid pool);
5442 in this case, only remove the moving field, but not the target */
5444 RemoveField(oldx, oldy);
5446 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5448 TEST_DrawLevelField(oldx, oldy);
5453 else if (element == EL_BLOCKED)
5455 Blocked2Moving(x, y, &oldx, &oldy);
5456 if (!IS_MOVING(oldx, oldy))
5460 if (element == EL_BLOCKED &&
5461 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5462 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5463 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5464 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5465 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5466 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5467 next_element = get_next_element(Feld[oldx][oldy]);
5469 RemoveField(oldx, oldy);
5470 RemoveField(newx, newy);
5472 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5474 if (next_element != EL_UNDEFINED)
5475 Feld[oldx][oldy] = next_element;
5477 TEST_DrawLevelField(oldx, oldy);
5478 TEST_DrawLevelField(newx, newy);
5481 void DrawDynamite(int x, int y)
5483 int sx = SCREENX(x), sy = SCREENY(y);
5484 int graphic = el2img(Feld[x][y]);
5487 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5490 if (IS_WALKABLE_INSIDE(Back[x][y]))
5494 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5495 else if (Store[x][y])
5496 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5498 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5500 if (Back[x][y] || Store[x][y])
5501 DrawGraphicThruMask(sx, sy, graphic, frame);
5503 DrawGraphic(sx, sy, graphic, frame);
5506 void CheckDynamite(int x, int y)
5508 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5512 if (MovDelay[x][y] != 0)
5515 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5521 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5526 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5528 boolean num_checked_players = 0;
5531 for (i = 0; i < MAX_PLAYERS; i++)
5533 if (stored_player[i].active)
5535 int sx = stored_player[i].jx;
5536 int sy = stored_player[i].jy;
5538 if (num_checked_players == 0)
5545 *sx1 = MIN(*sx1, sx);
5546 *sy1 = MIN(*sy1, sy);
5547 *sx2 = MAX(*sx2, sx);
5548 *sy2 = MAX(*sy2, sy);
5551 num_checked_players++;
5556 static boolean checkIfAllPlayersFitToScreen_RND()
5558 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5560 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5562 return (sx2 - sx1 < SCR_FIELDX &&
5563 sy2 - sy1 < SCR_FIELDY);
5566 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5568 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5570 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5572 *sx = (sx1 + sx2) / 2;
5573 *sy = (sy1 + sy2) / 2;
5576 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5577 boolean center_screen, boolean quick_relocation)
5579 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5580 boolean no_delay = (tape.warp_forward);
5581 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5582 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5584 if (quick_relocation)
5586 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5588 if (!level.shifted_relocation || center_screen)
5590 /* quick relocation (without scrolling), with centering of screen */
5592 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5593 x > SBX_Right + MIDPOSX ? SBX_Right :
5596 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5597 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5602 /* quick relocation (without scrolling), but do not center screen */
5604 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5605 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5608 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5609 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5612 int offset_x = x + (scroll_x - center_scroll_x);
5613 int offset_y = y + (scroll_y - center_scroll_y);
5615 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5616 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5617 offset_x - MIDPOSX);
5619 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5620 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5621 offset_y - MIDPOSY);
5627 if (!level.shifted_relocation || center_screen)
5629 /* quick relocation (without scrolling), with centering of screen */
5631 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5632 x > SBX_Right + MIDPOSX ? SBX_Right :
5635 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5636 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5641 /* quick relocation (without scrolling), but do not center screen */
5643 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5644 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5647 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5648 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651 int offset_x = x + (scroll_x - center_scroll_x);
5652 int offset_y = y + (scroll_y - center_scroll_y);
5654 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5655 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5656 offset_x - MIDPOSX);
5658 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5659 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5660 offset_y - MIDPOSY);
5663 /* quick relocation (without scrolling), inside visible screen area */
5665 int offset = game.scroll_delay_value;
5667 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5668 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5669 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5671 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5672 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5673 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5675 /* don't scroll over playfield boundaries */
5676 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5677 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5679 /* don't scroll over playfield boundaries */
5680 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5681 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5685 RedrawPlayfield(TRUE, 0,0,0,0);
5690 int scroll_xx, scroll_yy;
5692 if (!level.shifted_relocation || center_screen)
5694 /* visible relocation (with scrolling), with centering of screen */
5696 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5697 x > SBX_Right + MIDPOSX ? SBX_Right :
5700 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5701 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5706 /* visible relocation (with scrolling), but do not center screen */
5708 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5709 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5712 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5713 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5716 int offset_x = x + (scroll_x - center_scroll_x);
5717 int offset_y = y + (scroll_y - center_scroll_y);
5719 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5720 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5721 offset_x - MIDPOSX);
5723 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5724 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5725 offset_y - MIDPOSY);
5730 /* visible relocation (with scrolling), with centering of screen */
5732 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5733 x > SBX_Right + MIDPOSX ? SBX_Right :
5736 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5737 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5741 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5743 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5746 int fx = FX, fy = FY;
5748 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5749 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5751 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5757 fx += dx * TILEX / 2;
5758 fy += dy * TILEY / 2;
5760 ScrollLevel(dx, dy);
5763 /* scroll in two steps of half tile size to make things smoother */
5764 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5766 Delay(wait_delay_value);
5768 /* scroll second step to align at full tile size */
5770 Delay(wait_delay_value);
5775 Delay(wait_delay_value);
5779 void RelocatePlayer(int jx, int jy, int el_player_raw)
5781 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5782 int player_nr = GET_PLAYER_NR(el_player);
5783 struct PlayerInfo *player = &stored_player[player_nr];
5784 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5785 boolean no_delay = (tape.warp_forward);
5786 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5787 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5788 int old_jx = player->jx;
5789 int old_jy = player->jy;
5790 int old_element = Feld[old_jx][old_jy];
5791 int element = Feld[jx][jy];
5792 boolean player_relocated = (old_jx != jx || old_jy != jy);
5794 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5795 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5796 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5797 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5798 int leave_side_horiz = move_dir_horiz;
5799 int leave_side_vert = move_dir_vert;
5800 int enter_side = enter_side_horiz | enter_side_vert;
5801 int leave_side = leave_side_horiz | leave_side_vert;
5803 if (player->GameOver) /* do not reanimate dead player */
5806 if (!player_relocated) /* no need to relocate the player */
5809 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5811 RemoveField(jx, jy); /* temporarily remove newly placed player */
5812 DrawLevelField(jx, jy);
5815 if (player->present)
5817 while (player->MovPos)
5819 ScrollPlayer(player, SCROLL_GO_ON);
5820 ScrollScreen(NULL, SCROLL_GO_ON);
5822 AdvanceFrameAndPlayerCounters(player->index_nr);
5827 Delay(wait_delay_value);
5830 DrawPlayer(player); /* needed here only to cleanup last field */
5831 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5833 player->is_moving = FALSE;
5836 if (IS_CUSTOM_ELEMENT(old_element))
5837 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5839 player->index_bit, leave_side);
5841 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5843 player->index_bit, leave_side);
5845 Feld[jx][jy] = el_player;
5846 InitPlayerField(jx, jy, el_player, TRUE);
5848 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5849 possible that the relocation target field did not contain a player element,
5850 but a walkable element, to which the new player was relocated -- in this
5851 case, restore that (already initialized!) element on the player field */
5852 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5854 Feld[jx][jy] = element; /* restore previously existing element */
5856 /* !!! do not initialize already initialized element a second time !!! */
5857 /* (this causes at least problems with "element creation" CE trigger for
5858 already existing elements, and existing Sokoban fields counted twice) */
5859 InitField(jx, jy, FALSE);
5863 /* only visually relocate centered player */
5864 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5865 FALSE, level.instant_relocation);
5867 TestIfPlayerTouchesBadThing(jx, jy);
5868 TestIfPlayerTouchesCustomElement(jx, jy);
5870 if (IS_CUSTOM_ELEMENT(element))
5871 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5872 player->index_bit, enter_side);
5874 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5875 player->index_bit, enter_side);
5878 if (player->is_switching)
5880 /* ensure that relocation while still switching an element does not cause
5881 a new element to be treated as also switched directly after relocation
5882 (this is important for teleporter switches that teleport the player to
5883 a place where another teleporter switch is in the same direction, which
5884 would then incorrectly be treated as immediately switched before the
5885 direction key that caused the switch was released) */
5887 player->switch_x += jx - old_jx;
5888 player->switch_y += jy - old_jy;
5893 void Explode(int ex, int ey, int phase, int mode)
5899 /* !!! eliminate this variable !!! */
5900 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5902 if (game.explosions_delayed)
5904 ExplodeField[ex][ey] = mode;
5908 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5910 int center_element = Feld[ex][ey];
5911 int artwork_element, explosion_element; /* set these values later */
5914 /* --- This is only really needed (and now handled) in "Impact()". --- */
5915 /* do not explode moving elements that left the explode field in time */
5916 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5917 center_element == EL_EMPTY &&
5918 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5923 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5924 if (mode == EX_TYPE_NORMAL ||
5925 mode == EX_TYPE_CENTER ||
5926 mode == EX_TYPE_CROSS)
5927 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5930 /* remove things displayed in background while burning dynamite */
5931 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5934 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5936 /* put moving element to center field (and let it explode there) */
5937 center_element = MovingOrBlocked2Element(ex, ey);
5938 RemoveMovingField(ex, ey);
5939 Feld[ex][ey] = center_element;
5942 /* now "center_element" is finally determined -- set related values now */
5943 artwork_element = center_element; /* for custom player artwork */
5944 explosion_element = center_element; /* for custom player artwork */
5946 if (IS_PLAYER(ex, ey))
5948 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5950 artwork_element = stored_player[player_nr].artwork_element;
5952 if (level.use_explosion_element[player_nr])
5954 explosion_element = level.explosion_element[player_nr];
5955 artwork_element = explosion_element;
5960 if (mode == EX_TYPE_NORMAL ||
5961 mode == EX_TYPE_CENTER ||
5962 mode == EX_TYPE_CROSS)
5963 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5966 last_phase = element_info[explosion_element].explosion_delay + 1;
5968 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5970 int xx = x - ex + 1;
5971 int yy = y - ey + 1;
5974 if (!IN_LEV_FIELD(x, y) ||
5975 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5976 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5979 element = Feld[x][y];
5981 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5983 element = MovingOrBlocked2Element(x, y);
5985 if (!IS_EXPLOSION_PROOF(element))
5986 RemoveMovingField(x, y);
5989 /* indestructible elements can only explode in center (but not flames) */
5990 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5991 mode == EX_TYPE_BORDER)) ||
5992 element == EL_FLAMES)
5995 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5996 behaviour, for example when touching a yamyam that explodes to rocks
5997 with active deadly shield, a rock is created under the player !!! */
5998 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
6000 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6001 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6002 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6004 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6007 if (IS_ACTIVE_BOMB(element))
6009 /* re-activate things under the bomb like gate or penguin */
6010 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6017 /* save walkable background elements while explosion on same tile */
6018 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6019 (x != ex || y != ey || mode == EX_TYPE_BORDER))
6020 Back[x][y] = element;
6022 /* ignite explodable elements reached by other explosion */
6023 if (element == EL_EXPLOSION)
6024 element = Store2[x][y];
6026 if (AmoebaNr[x][y] &&
6027 (element == EL_AMOEBA_FULL ||
6028 element == EL_BD_AMOEBA ||
6029 element == EL_AMOEBA_GROWING))
6031 AmoebaCnt[AmoebaNr[x][y]]--;
6032 AmoebaCnt2[AmoebaNr[x][y]]--;
6037 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6039 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6041 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6043 if (PLAYERINFO(ex, ey)->use_murphy)
6044 Store[x][y] = EL_EMPTY;
6047 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6048 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6049 else if (ELEM_IS_PLAYER(center_element))
6050 Store[x][y] = EL_EMPTY;
6051 else if (center_element == EL_YAMYAM)
6052 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6053 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6054 Store[x][y] = element_info[center_element].content.e[xx][yy];
6056 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6057 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6058 otherwise) -- FIX THIS !!! */
6059 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6060 Store[x][y] = element_info[element].content.e[1][1];
6062 else if (!CAN_EXPLODE(element))
6063 Store[x][y] = element_info[element].content.e[1][1];
6066 Store[x][y] = EL_EMPTY;
6068 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6069 center_element == EL_AMOEBA_TO_DIAMOND)
6070 Store2[x][y] = element;
6072 Feld[x][y] = EL_EXPLOSION;
6073 GfxElement[x][y] = artwork_element;
6075 ExplodePhase[x][y] = 1;
6076 ExplodeDelay[x][y] = last_phase;
6081 if (center_element == EL_YAMYAM)
6082 game.yamyam_content_nr =
6083 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6095 GfxFrame[x][y] = 0; /* restart explosion animation */
6097 last_phase = ExplodeDelay[x][y];
6099 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6103 /* activate this even in non-DEBUG version until cause for crash in
6104 getGraphicAnimationFrame() (see below) is found and eliminated */
6110 /* this can happen if the player leaves an explosion just in time */
6111 if (GfxElement[x][y] == EL_UNDEFINED)
6112 GfxElement[x][y] = EL_EMPTY;
6114 if (GfxElement[x][y] == EL_UNDEFINED)
6117 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6118 printf("Explode(): This should never happen!\n");
6121 GfxElement[x][y] = EL_EMPTY;
6127 border_element = Store2[x][y];
6128 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6129 border_element = StorePlayer[x][y];
6131 if (phase == element_info[border_element].ignition_delay ||
6132 phase == last_phase)
6134 boolean border_explosion = FALSE;
6136 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6137 !PLAYER_EXPLOSION_PROTECTED(x, y))
6139 KillPlayerUnlessExplosionProtected(x, y);
6140 border_explosion = TRUE;
6142 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6144 Feld[x][y] = Store2[x][y];
6147 border_explosion = TRUE;
6149 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6151 AmoebeUmwandeln(x, y);
6153 border_explosion = TRUE;
6156 /* if an element just explodes due to another explosion (chain-reaction),
6157 do not immediately end the new explosion when it was the last frame of
6158 the explosion (as it would be done in the following "if"-statement!) */
6159 if (border_explosion && phase == last_phase)
6163 if (phase == last_phase)
6167 element = Feld[x][y] = Store[x][y];
6168 Store[x][y] = Store2[x][y] = 0;
6169 GfxElement[x][y] = EL_UNDEFINED;
6171 /* player can escape from explosions and might therefore be still alive */
6172 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6173 element <= EL_PLAYER_IS_EXPLODING_4)
6175 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6176 int explosion_element = EL_PLAYER_1 + player_nr;
6177 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6178 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6180 if (level.use_explosion_element[player_nr])
6181 explosion_element = level.explosion_element[player_nr];
6183 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6184 element_info[explosion_element].content.e[xx][yy]);
6187 /* restore probably existing indestructible background element */
6188 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6189 element = Feld[x][y] = Back[x][y];
6192 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6193 GfxDir[x][y] = MV_NONE;
6194 ChangeDelay[x][y] = 0;
6195 ChangePage[x][y] = -1;
6197 #if USE_NEW_CUSTOM_VALUE
6198 CustomValue[x][y] = 0;
6201 InitField_WithBug2(x, y, FALSE);
6203 TEST_DrawLevelField(x, y);
6205 TestIfElementTouchesCustomElement(x, y);
6207 if (GFX_CRUMBLED(element))
6208 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6210 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6211 StorePlayer[x][y] = 0;
6213 if (ELEM_IS_PLAYER(element))
6214 RelocatePlayer(x, y, element);
6216 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6218 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6219 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6222 TEST_DrawLevelFieldCrumbled(x, y);
6224 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6226 DrawLevelElement(x, y, Back[x][y]);
6227 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6229 else if (IS_WALKABLE_UNDER(Back[x][y]))
6231 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6232 DrawLevelElementThruMask(x, y, Back[x][y]);
6234 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6235 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6239 void DynaExplode(int ex, int ey)
6242 int dynabomb_element = Feld[ex][ey];
6243 int dynabomb_size = 1;
6244 boolean dynabomb_xl = FALSE;
6245 struct PlayerInfo *player;
6246 static int xy[4][2] =
6254 if (IS_ACTIVE_BOMB(dynabomb_element))
6256 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6257 dynabomb_size = player->dynabomb_size;
6258 dynabomb_xl = player->dynabomb_xl;
6259 player->dynabombs_left++;
6262 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6264 for (i = 0; i < NUM_DIRECTIONS; i++)
6266 for (j = 1; j <= dynabomb_size; j++)
6268 int x = ex + j * xy[i][0];
6269 int y = ey + j * xy[i][1];
6272 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6275 element = Feld[x][y];
6277 /* do not restart explosions of fields with active bombs */
6278 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6281 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6283 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6284 !IS_DIGGABLE(element) && !dynabomb_xl)
6290 void Bang(int x, int y)
6292 int element = MovingOrBlocked2Element(x, y);
6293 int explosion_type = EX_TYPE_NORMAL;
6295 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6297 struct PlayerInfo *player = PLAYERINFO(x, y);
6299 #if USE_FIX_CE_ACTION_WITH_PLAYER
6300 element = Feld[x][y] = player->initial_element;
6302 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6303 player->element_nr);
6306 if (level.use_explosion_element[player->index_nr])
6308 int explosion_element = level.explosion_element[player->index_nr];
6310 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6311 explosion_type = EX_TYPE_CROSS;
6312 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6313 explosion_type = EX_TYPE_CENTER;
6321 case EL_BD_BUTTERFLY:
6324 case EL_DARK_YAMYAM:
6328 RaiseScoreElement(element);
6331 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6332 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6333 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6334 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6335 case EL_DYNABOMB_INCREASE_NUMBER:
6336 case EL_DYNABOMB_INCREASE_SIZE:
6337 case EL_DYNABOMB_INCREASE_POWER:
6338 explosion_type = EX_TYPE_DYNA;
6341 case EL_DC_LANDMINE:
6343 case EL_EM_EXIT_OPEN:
6344 case EL_EM_STEEL_EXIT_OPEN:
6346 explosion_type = EX_TYPE_CENTER;
6351 case EL_LAMP_ACTIVE:
6352 case EL_AMOEBA_TO_DIAMOND:
6353 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6354 explosion_type = EX_TYPE_CENTER;
6358 if (element_info[element].explosion_type == EXPLODES_CROSS)
6359 explosion_type = EX_TYPE_CROSS;
6360 else if (element_info[element].explosion_type == EXPLODES_1X1)
6361 explosion_type = EX_TYPE_CENTER;
6365 if (explosion_type == EX_TYPE_DYNA)
6368 Explode(x, y, EX_PHASE_START, explosion_type);
6370 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6373 void SplashAcid(int x, int y)
6375 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6376 (!IN_LEV_FIELD(x - 1, y - 2) ||
6377 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6378 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6380 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6381 (!IN_LEV_FIELD(x + 1, y - 2) ||
6382 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6383 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6385 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6388 static void InitBeltMovement()
6390 static int belt_base_element[4] =
6392 EL_CONVEYOR_BELT_1_LEFT,
6393 EL_CONVEYOR_BELT_2_LEFT,
6394 EL_CONVEYOR_BELT_3_LEFT,
6395 EL_CONVEYOR_BELT_4_LEFT
6397 static int belt_base_active_element[4] =
6399 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6400 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6401 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6402 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6407 /* set frame order for belt animation graphic according to belt direction */
6408 for (i = 0; i < NUM_BELTS; i++)
6412 for (j = 0; j < NUM_BELT_PARTS; j++)
6414 int element = belt_base_active_element[belt_nr] + j;
6415 int graphic_1 = el2img(element);
6416 int graphic_2 = el2panelimg(element);
6418 if (game.belt_dir[i] == MV_LEFT)
6420 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6421 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6425 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6426 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6431 SCAN_PLAYFIELD(x, y)
6433 int element = Feld[x][y];
6435 for (i = 0; i < NUM_BELTS; i++)
6437 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6439 int e_belt_nr = getBeltNrFromBeltElement(element);
6442 if (e_belt_nr == belt_nr)
6444 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6446 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6453 static void ToggleBeltSwitch(int x, int y)
6455 static int belt_base_element[4] =
6457 EL_CONVEYOR_BELT_1_LEFT,
6458 EL_CONVEYOR_BELT_2_LEFT,
6459 EL_CONVEYOR_BELT_3_LEFT,
6460 EL_CONVEYOR_BELT_4_LEFT
6462 static int belt_base_active_element[4] =
6464 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6465 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6466 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6467 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6469 static int belt_base_switch_element[4] =
6471 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6472 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6473 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6474 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6476 static int belt_move_dir[4] =
6484 int element = Feld[x][y];
6485 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6486 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6487 int belt_dir = belt_move_dir[belt_dir_nr];
6490 if (!IS_BELT_SWITCH(element))
6493 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6494 game.belt_dir[belt_nr] = belt_dir;
6496 if (belt_dir_nr == 3)
6499 /* set frame order for belt animation graphic according to belt direction */
6500 for (i = 0; i < NUM_BELT_PARTS; i++)
6502 int element = belt_base_active_element[belt_nr] + i;
6503 int graphic_1 = el2img(element);
6504 int graphic_2 = el2panelimg(element);
6506 if (belt_dir == MV_LEFT)
6508 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6509 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6513 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6514 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6518 SCAN_PLAYFIELD(xx, yy)
6520 int element = Feld[xx][yy];
6522 if (IS_BELT_SWITCH(element))
6524 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6526 if (e_belt_nr == belt_nr)
6528 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6529 TEST_DrawLevelField(xx, yy);
6532 else if (IS_BELT(element) && belt_dir != MV_NONE)
6534 int e_belt_nr = getBeltNrFromBeltElement(element);
6536 if (e_belt_nr == belt_nr)
6538 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6540 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6541 TEST_DrawLevelField(xx, yy);
6544 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6546 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6548 if (e_belt_nr == belt_nr)
6550 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6552 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6553 TEST_DrawLevelField(xx, yy);
6559 static void ToggleSwitchgateSwitch(int x, int y)
6563 game.switchgate_pos = !game.switchgate_pos;
6565 SCAN_PLAYFIELD(xx, yy)
6567 int element = Feld[xx][yy];
6569 #if !USE_BOTH_SWITCHGATE_SWITCHES
6570 if (element == EL_SWITCHGATE_SWITCH_UP ||
6571 element == EL_SWITCHGATE_SWITCH_DOWN)
6573 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6574 TEST_DrawLevelField(xx, yy);
6576 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6577 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6579 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6580 TEST_DrawLevelField(xx, yy);
6583 if (element == EL_SWITCHGATE_SWITCH_UP)
6585 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6586 TEST_DrawLevelField(xx, yy);
6588 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6590 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6591 TEST_DrawLevelField(xx, yy);
6593 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6595 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6596 TEST_DrawLevelField(xx, yy);
6598 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6600 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6601 TEST_DrawLevelField(xx, yy);
6604 else if (element == EL_SWITCHGATE_OPEN ||
6605 element == EL_SWITCHGATE_OPENING)
6607 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6609 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6611 else if (element == EL_SWITCHGATE_CLOSED ||
6612 element == EL_SWITCHGATE_CLOSING)
6614 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6616 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6621 static int getInvisibleActiveFromInvisibleElement(int element)
6623 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6624 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6625 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6629 static int getInvisibleFromInvisibleActiveElement(int element)
6631 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6632 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6633 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6637 static void RedrawAllLightSwitchesAndInvisibleElements()
6641 SCAN_PLAYFIELD(x, y)
6643 int element = Feld[x][y];
6645 if (element == EL_LIGHT_SWITCH &&
6646 game.light_time_left > 0)
6648 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6649 TEST_DrawLevelField(x, y);
6651 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6652 game.light_time_left == 0)
6654 Feld[x][y] = EL_LIGHT_SWITCH;
6655 TEST_DrawLevelField(x, y);
6657 else if (element == EL_EMC_DRIPPER &&
6658 game.light_time_left > 0)
6660 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6661 TEST_DrawLevelField(x, y);
6663 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6664 game.light_time_left == 0)
6666 Feld[x][y] = EL_EMC_DRIPPER;
6667 TEST_DrawLevelField(x, y);
6669 else if (element == EL_INVISIBLE_STEELWALL ||
6670 element == EL_INVISIBLE_WALL ||
6671 element == EL_INVISIBLE_SAND)
6673 if (game.light_time_left > 0)
6674 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6676 TEST_DrawLevelField(x, y);
6678 /* uncrumble neighbour fields, if needed */
6679 if (element == EL_INVISIBLE_SAND)
6680 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6682 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6683 element == EL_INVISIBLE_WALL_ACTIVE ||
6684 element == EL_INVISIBLE_SAND_ACTIVE)
6686 if (game.light_time_left == 0)
6687 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6689 TEST_DrawLevelField(x, y);
6691 /* re-crumble neighbour fields, if needed */
6692 if (element == EL_INVISIBLE_SAND)
6693 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6698 static void RedrawAllInvisibleElementsForLenses()
6702 SCAN_PLAYFIELD(x, y)
6704 int element = Feld[x][y];
6706 if (element == EL_EMC_DRIPPER &&
6707 game.lenses_time_left > 0)
6709 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6710 TEST_DrawLevelField(x, y);
6712 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6713 game.lenses_time_left == 0)
6715 Feld[x][y] = EL_EMC_DRIPPER;
6716 TEST_DrawLevelField(x, y);
6718 else if (element == EL_INVISIBLE_STEELWALL ||
6719 element == EL_INVISIBLE_WALL ||
6720 element == EL_INVISIBLE_SAND)
6722 if (game.lenses_time_left > 0)
6723 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6725 TEST_DrawLevelField(x, y);
6727 /* uncrumble neighbour fields, if needed */
6728 if (element == EL_INVISIBLE_SAND)
6729 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6731 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6732 element == EL_INVISIBLE_WALL_ACTIVE ||
6733 element == EL_INVISIBLE_SAND_ACTIVE)
6735 if (game.lenses_time_left == 0)
6736 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6738 TEST_DrawLevelField(x, y);
6740 /* re-crumble neighbour fields, if needed */
6741 if (element == EL_INVISIBLE_SAND)
6742 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6747 static void RedrawAllInvisibleElementsForMagnifier()
6751 SCAN_PLAYFIELD(x, y)
6753 int element = Feld[x][y];
6755 if (element == EL_EMC_FAKE_GRASS &&
6756 game.magnify_time_left > 0)
6758 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6759 TEST_DrawLevelField(x, y);
6761 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6762 game.magnify_time_left == 0)
6764 Feld[x][y] = EL_EMC_FAKE_GRASS;
6765 TEST_DrawLevelField(x, y);
6767 else if (IS_GATE_GRAY(element) &&
6768 game.magnify_time_left > 0)
6770 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6771 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6772 IS_EM_GATE_GRAY(element) ?
6773 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6774 IS_EMC_GATE_GRAY(element) ?
6775 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6776 IS_DC_GATE_GRAY(element) ?
6777 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6779 TEST_DrawLevelField(x, y);
6781 else if (IS_GATE_GRAY_ACTIVE(element) &&
6782 game.magnify_time_left == 0)
6784 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6785 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6786 IS_EM_GATE_GRAY_ACTIVE(element) ?
6787 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6788 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6789 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6790 IS_DC_GATE_GRAY_ACTIVE(element) ?
6791 EL_DC_GATE_WHITE_GRAY :
6793 TEST_DrawLevelField(x, y);
6798 static void ToggleLightSwitch(int x, int y)
6800 int element = Feld[x][y];
6802 game.light_time_left =
6803 (element == EL_LIGHT_SWITCH ?
6804 level.time_light * FRAMES_PER_SECOND : 0);
6806 RedrawAllLightSwitchesAndInvisibleElements();
6809 static void ActivateTimegateSwitch(int x, int y)
6813 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6815 SCAN_PLAYFIELD(xx, yy)
6817 int element = Feld[xx][yy];
6819 if (element == EL_TIMEGATE_CLOSED ||
6820 element == EL_TIMEGATE_CLOSING)
6822 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6823 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6827 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6829 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6830 TEST_DrawLevelField(xx, yy);
6837 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6838 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6840 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6844 void Impact(int x, int y)
6846 boolean last_line = (y == lev_fieldy - 1);
6847 boolean object_hit = FALSE;
6848 boolean impact = (last_line || object_hit);
6849 int element = Feld[x][y];
6850 int smashed = EL_STEELWALL;
6852 if (!last_line) /* check if element below was hit */
6854 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6857 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6858 MovDir[x][y + 1] != MV_DOWN ||
6859 MovPos[x][y + 1] <= TILEY / 2));
6861 /* do not smash moving elements that left the smashed field in time */
6862 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6863 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6866 #if USE_QUICKSAND_IMPACT_BUGFIX
6867 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6869 RemoveMovingField(x, y + 1);
6870 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6871 Feld[x][y + 2] = EL_ROCK;
6872 TEST_DrawLevelField(x, y + 2);
6877 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6879 RemoveMovingField(x, y + 1);
6880 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6881 Feld[x][y + 2] = EL_ROCK;
6882 TEST_DrawLevelField(x, y + 2);
6889 smashed = MovingOrBlocked2Element(x, y + 1);
6891 impact = (last_line || object_hit);
6894 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6896 SplashAcid(x, y + 1);
6900 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6901 /* only reset graphic animation if graphic really changes after impact */
6903 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6905 ResetGfxAnimation(x, y);
6906 TEST_DrawLevelField(x, y);
6909 if (impact && CAN_EXPLODE_IMPACT(element))
6914 else if (impact && element == EL_PEARL &&
6915 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6917 ResetGfxAnimation(x, y);
6919 Feld[x][y] = EL_PEARL_BREAKING;
6920 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6923 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6925 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6930 if (impact && element == EL_AMOEBA_DROP)
6932 if (object_hit && IS_PLAYER(x, y + 1))
6933 KillPlayerUnlessEnemyProtected(x, y + 1);
6934 else if (object_hit && smashed == EL_PENGUIN)
6938 Feld[x][y] = EL_AMOEBA_GROWING;
6939 Store[x][y] = EL_AMOEBA_WET;
6941 ResetRandomAnimationValue(x, y);
6946 if (object_hit) /* check which object was hit */
6948 if ((CAN_PASS_MAGIC_WALL(element) &&
6949 (smashed == EL_MAGIC_WALL ||
6950 smashed == EL_BD_MAGIC_WALL)) ||
6951 (CAN_PASS_DC_MAGIC_WALL(element) &&
6952 smashed == EL_DC_MAGIC_WALL))
6955 int activated_magic_wall =
6956 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6957 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6958 EL_DC_MAGIC_WALL_ACTIVE);
6960 /* activate magic wall / mill */
6961 SCAN_PLAYFIELD(xx, yy)
6963 if (Feld[xx][yy] == smashed)
6964 Feld[xx][yy] = activated_magic_wall;
6967 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6968 game.magic_wall_active = TRUE;
6970 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6971 SND_MAGIC_WALL_ACTIVATING :
6972 smashed == EL_BD_MAGIC_WALL ?
6973 SND_BD_MAGIC_WALL_ACTIVATING :
6974 SND_DC_MAGIC_WALL_ACTIVATING));
6977 if (IS_PLAYER(x, y + 1))
6979 if (CAN_SMASH_PLAYER(element))
6981 KillPlayerUnlessEnemyProtected(x, y + 1);
6985 else if (smashed == EL_PENGUIN)
6987 if (CAN_SMASH_PLAYER(element))
6993 else if (element == EL_BD_DIAMOND)
6995 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7001 else if (((element == EL_SP_INFOTRON ||
7002 element == EL_SP_ZONK) &&
7003 (smashed == EL_SP_SNIKSNAK ||
7004 smashed == EL_SP_ELECTRON ||
7005 smashed == EL_SP_DISK_ORANGE)) ||
7006 (element == EL_SP_INFOTRON &&
7007 smashed == EL_SP_DISK_YELLOW))
7012 else if (CAN_SMASH_EVERYTHING(element))
7014 if (IS_CLASSIC_ENEMY(smashed) ||
7015 CAN_EXPLODE_SMASHED(smashed))
7020 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7022 if (smashed == EL_LAMP ||
7023 smashed == EL_LAMP_ACTIVE)
7028 else if (smashed == EL_NUT)
7030 Feld[x][y + 1] = EL_NUT_BREAKING;
7031 PlayLevelSound(x, y, SND_NUT_BREAKING);
7032 RaiseScoreElement(EL_NUT);
7035 else if (smashed == EL_PEARL)
7037 ResetGfxAnimation(x, y);
7039 Feld[x][y + 1] = EL_PEARL_BREAKING;
7040 PlayLevelSound(x, y, SND_PEARL_BREAKING);
7043 else if (smashed == EL_DIAMOND)
7045 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7046 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7049 else if (IS_BELT_SWITCH(smashed))
7051 ToggleBeltSwitch(x, y + 1);
7053 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7054 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7055 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7056 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7058 ToggleSwitchgateSwitch(x, y + 1);
7060 else if (smashed == EL_LIGHT_SWITCH ||
7061 smashed == EL_LIGHT_SWITCH_ACTIVE)
7063 ToggleLightSwitch(x, y + 1);
7068 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7071 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7073 CheckElementChangeBySide(x, y + 1, smashed, element,
7074 CE_SWITCHED, CH_SIDE_TOP);
7075 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7081 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7086 /* play sound of magic wall / mill */
7088 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7089 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7090 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7092 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7093 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7094 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7095 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7096 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7097 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7102 /* play sound of object that hits the ground */
7103 if (last_line || object_hit)
7104 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7107 inline static void TurnRoundExt(int x, int y)
7119 { 0, 0 }, { 0, 0 }, { 0, 0 },
7124 int left, right, back;
7128 { MV_DOWN, MV_UP, MV_RIGHT },
7129 { MV_UP, MV_DOWN, MV_LEFT },
7131 { MV_LEFT, MV_RIGHT, MV_DOWN },
7135 { MV_RIGHT, MV_LEFT, MV_UP }
7138 int element = Feld[x][y];
7139 int move_pattern = element_info[element].move_pattern;
7141 int old_move_dir = MovDir[x][y];
7142 int left_dir = turn[old_move_dir].left;
7143 int right_dir = turn[old_move_dir].right;
7144 int back_dir = turn[old_move_dir].back;
7146 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7147 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7148 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7149 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7151 int left_x = x + left_dx, left_y = y + left_dy;
7152 int right_x = x + right_dx, right_y = y + right_dy;
7153 int move_x = x + move_dx, move_y = y + move_dy;
7157 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7159 TestIfBadThingTouchesOtherBadThing(x, y);
7161 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7162 MovDir[x][y] = right_dir;
7163 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7164 MovDir[x][y] = left_dir;
7166 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7168 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7171 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7173 TestIfBadThingTouchesOtherBadThing(x, y);
7175 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7176 MovDir[x][y] = left_dir;
7177 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7178 MovDir[x][y] = right_dir;
7180 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7182 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7185 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7187 TestIfBadThingTouchesOtherBadThing(x, y);
7189 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7190 MovDir[x][y] = left_dir;
7191 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7192 MovDir[x][y] = right_dir;
7194 if (MovDir[x][y] != old_move_dir)
7197 else if (element == EL_YAMYAM)
7199 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7200 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7202 if (can_turn_left && can_turn_right)
7203 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7204 else if (can_turn_left)
7205 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7206 else if (can_turn_right)
7207 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7209 MovDir[x][y] = back_dir;
7211 MovDelay[x][y] = 16 + 16 * RND(3);
7213 else if (element == EL_DARK_YAMYAM)
7215 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7217 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7220 if (can_turn_left && can_turn_right)
7221 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7222 else if (can_turn_left)
7223 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7224 else if (can_turn_right)
7225 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7227 MovDir[x][y] = back_dir;
7229 MovDelay[x][y] = 16 + 16 * RND(3);
7231 else if (element == EL_PACMAN)
7233 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7234 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7236 if (can_turn_left && can_turn_right)
7237 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7238 else if (can_turn_left)
7239 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7240 else if (can_turn_right)
7241 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7243 MovDir[x][y] = back_dir;
7245 MovDelay[x][y] = 6 + RND(40);
7247 else if (element == EL_PIG)
7249 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7250 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7251 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7252 boolean should_turn_left, should_turn_right, should_move_on;
7254 int rnd = RND(rnd_value);
7256 should_turn_left = (can_turn_left &&
7258 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7259 y + back_dy + left_dy)));
7260 should_turn_right = (can_turn_right &&
7262 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7263 y + back_dy + right_dy)));
7264 should_move_on = (can_move_on &&
7267 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7268 y + move_dy + left_dy) ||
7269 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7270 y + move_dy + right_dy)));
7272 if (should_turn_left || should_turn_right || should_move_on)
7274 if (should_turn_left && should_turn_right && should_move_on)
7275 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7276 rnd < 2 * rnd_value / 3 ? right_dir :
7278 else if (should_turn_left && should_turn_right)
7279 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7280 else if (should_turn_left && should_move_on)
7281 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7282 else if (should_turn_right && should_move_on)
7283 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7284 else if (should_turn_left)
7285 MovDir[x][y] = left_dir;
7286 else if (should_turn_right)
7287 MovDir[x][y] = right_dir;
7288 else if (should_move_on)
7289 MovDir[x][y] = old_move_dir;
7291 else if (can_move_on && rnd > rnd_value / 8)
7292 MovDir[x][y] = old_move_dir;
7293 else if (can_turn_left && can_turn_right)
7294 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7295 else if (can_turn_left && rnd > rnd_value / 8)
7296 MovDir[x][y] = left_dir;
7297 else if (can_turn_right && rnd > rnd_value/8)
7298 MovDir[x][y] = right_dir;
7300 MovDir[x][y] = back_dir;
7302 xx = x + move_xy[MovDir[x][y]].dx;
7303 yy = y + move_xy[MovDir[x][y]].dy;
7305 if (!IN_LEV_FIELD(xx, yy) ||
7306 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7307 MovDir[x][y] = old_move_dir;
7311 else if (element == EL_DRAGON)
7313 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7314 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7315 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7317 int rnd = RND(rnd_value);
7319 if (can_move_on && rnd > rnd_value / 8)
7320 MovDir[x][y] = old_move_dir;
7321 else if (can_turn_left && can_turn_right)
7322 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7323 else if (can_turn_left && rnd > rnd_value / 8)
7324 MovDir[x][y] = left_dir;
7325 else if (can_turn_right && rnd > rnd_value / 8)
7326 MovDir[x][y] = right_dir;
7328 MovDir[x][y] = back_dir;
7330 xx = x + move_xy[MovDir[x][y]].dx;
7331 yy = y + move_xy[MovDir[x][y]].dy;
7333 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7334 MovDir[x][y] = old_move_dir;
7338 else if (element == EL_MOLE)
7340 boolean can_move_on =
7341 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7342 IS_AMOEBOID(Feld[move_x][move_y]) ||
7343 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7346 boolean can_turn_left =
7347 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7348 IS_AMOEBOID(Feld[left_x][left_y])));
7350 boolean can_turn_right =
7351 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7352 IS_AMOEBOID(Feld[right_x][right_y])));
7354 if (can_turn_left && can_turn_right)
7355 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7356 else if (can_turn_left)
7357 MovDir[x][y] = left_dir;
7359 MovDir[x][y] = right_dir;
7362 if (MovDir[x][y] != old_move_dir)
7365 else if (element == EL_BALLOON)
7367 MovDir[x][y] = game.wind_direction;
7370 else if (element == EL_SPRING)
7372 #if USE_NEW_SPRING_BUMPER
7373 if (MovDir[x][y] & MV_HORIZONTAL)
7375 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7376 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7378 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7379 ResetGfxAnimation(move_x, move_y);
7380 TEST_DrawLevelField(move_x, move_y);
7382 MovDir[x][y] = back_dir;
7384 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7385 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7386 MovDir[x][y] = MV_NONE;
7389 if (MovDir[x][y] & MV_HORIZONTAL &&
7390 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7391 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7392 MovDir[x][y] = MV_NONE;
7397 else if (element == EL_ROBOT ||
7398 element == EL_SATELLITE ||
7399 element == EL_PENGUIN ||
7400 element == EL_EMC_ANDROID)
7402 int attr_x = -1, attr_y = -1;
7413 for (i = 0; i < MAX_PLAYERS; i++)
7415 struct PlayerInfo *player = &stored_player[i];
7416 int jx = player->jx, jy = player->jy;
7418 if (!player->active)
7422 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7430 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7431 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7432 game.engine_version < VERSION_IDENT(3,1,0,0)))
7438 if (element == EL_PENGUIN)
7441 static int xy[4][2] =
7449 for (i = 0; i < NUM_DIRECTIONS; i++)
7451 int ex = x + xy[i][0];
7452 int ey = y + xy[i][1];
7454 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7455 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7456 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7457 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7466 MovDir[x][y] = MV_NONE;
7468 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7469 else if (attr_x > x)
7470 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7472 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7473 else if (attr_y > y)
7474 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7476 if (element == EL_ROBOT)
7480 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7481 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7482 Moving2Blocked(x, y, &newx, &newy);
7484 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7485 MovDelay[x][y] = 8 + 8 * !RND(3);
7487 MovDelay[x][y] = 16;
7489 else if (element == EL_PENGUIN)
7495 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7497 boolean first_horiz = RND(2);
7498 int new_move_dir = MovDir[x][y];
7501 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502 Moving2Blocked(x, y, &newx, &newy);
7504 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7508 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509 Moving2Blocked(x, y, &newx, &newy);
7511 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7514 MovDir[x][y] = old_move_dir;
7518 else if (element == EL_SATELLITE)
7524 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7526 boolean first_horiz = RND(2);
7527 int new_move_dir = MovDir[x][y];
7530 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7531 Moving2Blocked(x, y, &newx, &newy);
7533 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7537 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7538 Moving2Blocked(x, y, &newx, &newy);
7540 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7543 MovDir[x][y] = old_move_dir;
7547 else if (element == EL_EMC_ANDROID)
7549 static int check_pos[16] =
7551 -1, /* 0 => (invalid) */
7552 7, /* 1 => MV_LEFT */
7553 3, /* 2 => MV_RIGHT */
7554 -1, /* 3 => (invalid) */
7556 0, /* 5 => MV_LEFT | MV_UP */
7557 2, /* 6 => MV_RIGHT | MV_UP */
7558 -1, /* 7 => (invalid) */
7559 5, /* 8 => MV_DOWN */
7560 6, /* 9 => MV_LEFT | MV_DOWN */
7561 4, /* 10 => MV_RIGHT | MV_DOWN */
7562 -1, /* 11 => (invalid) */
7563 -1, /* 12 => (invalid) */
7564 -1, /* 13 => (invalid) */
7565 -1, /* 14 => (invalid) */
7566 -1, /* 15 => (invalid) */
7574 { -1, -1, MV_LEFT | MV_UP },
7576 { +1, -1, MV_RIGHT | MV_UP },
7577 { +1, 0, MV_RIGHT },
7578 { +1, +1, MV_RIGHT | MV_DOWN },
7580 { -1, +1, MV_LEFT | MV_DOWN },
7583 int start_pos, check_order;
7584 boolean can_clone = FALSE;
7587 /* check if there is any free field around current position */
7588 for (i = 0; i < 8; i++)
7590 int newx = x + check_xy[i].dx;
7591 int newy = y + check_xy[i].dy;
7593 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7601 if (can_clone) /* randomly find an element to clone */
7605 start_pos = check_pos[RND(8)];
7606 check_order = (RND(2) ? -1 : +1);
7608 for (i = 0; i < 8; i++)
7610 int pos_raw = start_pos + i * check_order;
7611 int pos = (pos_raw + 8) % 8;
7612 int newx = x + check_xy[pos].dx;
7613 int newy = y + check_xy[pos].dy;
7615 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7617 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7618 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7620 Store[x][y] = Feld[newx][newy];
7629 if (can_clone) /* randomly find a direction to move */
7633 start_pos = check_pos[RND(8)];
7634 check_order = (RND(2) ? -1 : +1);
7636 for (i = 0; i < 8; i++)
7638 int pos_raw = start_pos + i * check_order;
7639 int pos = (pos_raw + 8) % 8;
7640 int newx = x + check_xy[pos].dx;
7641 int newy = y + check_xy[pos].dy;
7642 int new_move_dir = check_xy[pos].dir;
7644 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7646 MovDir[x][y] = new_move_dir;
7647 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7656 if (can_clone) /* cloning and moving successful */
7659 /* cannot clone -- try to move towards player */
7661 start_pos = check_pos[MovDir[x][y] & 0x0f];
7662 check_order = (RND(2) ? -1 : +1);
7664 for (i = 0; i < 3; i++)
7666 /* first check start_pos, then previous/next or (next/previous) pos */
7667 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7668 int pos = (pos_raw + 8) % 8;
7669 int newx = x + check_xy[pos].dx;
7670 int newy = y + check_xy[pos].dy;
7671 int new_move_dir = check_xy[pos].dir;
7673 if (IS_PLAYER(newx, newy))
7676 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7678 MovDir[x][y] = new_move_dir;
7679 MovDelay[x][y] = level.android_move_time * 8 + 1;
7686 else if (move_pattern == MV_TURNING_LEFT ||
7687 move_pattern == MV_TURNING_RIGHT ||
7688 move_pattern == MV_TURNING_LEFT_RIGHT ||
7689 move_pattern == MV_TURNING_RIGHT_LEFT ||
7690 move_pattern == MV_TURNING_RANDOM ||
7691 move_pattern == MV_ALL_DIRECTIONS)
7693 boolean can_turn_left =
7694 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7695 boolean can_turn_right =
7696 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7698 if (element_info[element].move_stepsize == 0) /* "not moving" */
7701 if (move_pattern == MV_TURNING_LEFT)
7702 MovDir[x][y] = left_dir;
7703 else if (move_pattern == MV_TURNING_RIGHT)
7704 MovDir[x][y] = right_dir;
7705 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7706 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7707 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7708 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7709 else if (move_pattern == MV_TURNING_RANDOM)
7710 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7711 can_turn_right && !can_turn_left ? right_dir :
7712 RND(2) ? left_dir : right_dir);
7713 else if (can_turn_left && can_turn_right)
7714 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7715 else if (can_turn_left)
7716 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7717 else if (can_turn_right)
7718 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7720 MovDir[x][y] = back_dir;
7722 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7724 else if (move_pattern == MV_HORIZONTAL ||
7725 move_pattern == MV_VERTICAL)
7727 if (move_pattern & old_move_dir)
7728 MovDir[x][y] = back_dir;
7729 else if (move_pattern == MV_HORIZONTAL)
7730 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7731 else if (move_pattern == MV_VERTICAL)
7732 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7734 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7736 else if (move_pattern & MV_ANY_DIRECTION)
7738 MovDir[x][y] = move_pattern;
7739 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7741 else if (move_pattern & MV_WIND_DIRECTION)
7743 MovDir[x][y] = game.wind_direction;
7744 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7746 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7748 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7749 MovDir[x][y] = left_dir;
7750 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7751 MovDir[x][y] = right_dir;
7753 if (MovDir[x][y] != old_move_dir)
7754 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7758 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7759 MovDir[x][y] = right_dir;
7760 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7761 MovDir[x][y] = left_dir;
7763 if (MovDir[x][y] != old_move_dir)
7764 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7766 else if (move_pattern == MV_TOWARDS_PLAYER ||
7767 move_pattern == MV_AWAY_FROM_PLAYER)
7769 int attr_x = -1, attr_y = -1;
7771 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7782 for (i = 0; i < MAX_PLAYERS; i++)
7784 struct PlayerInfo *player = &stored_player[i];
7785 int jx = player->jx, jy = player->jy;
7787 if (!player->active)
7791 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7799 MovDir[x][y] = MV_NONE;
7801 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7802 else if (attr_x > x)
7803 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7805 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7806 else if (attr_y > y)
7807 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7809 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7811 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7813 boolean first_horiz = RND(2);
7814 int new_move_dir = MovDir[x][y];
7816 if (element_info[element].move_stepsize == 0) /* "not moving" */
7818 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7819 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7825 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7826 Moving2Blocked(x, y, &newx, &newy);
7828 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7832 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7833 Moving2Blocked(x, y, &newx, &newy);
7835 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7838 MovDir[x][y] = old_move_dir;
7841 else if (move_pattern == MV_WHEN_PUSHED ||
7842 move_pattern == MV_WHEN_DROPPED)
7844 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7845 MovDir[x][y] = MV_NONE;
7849 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7851 static int test_xy[7][2] =
7861 static int test_dir[7] =
7871 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7872 int move_preference = -1000000; /* start with very low preference */
7873 int new_move_dir = MV_NONE;
7874 int start_test = RND(4);
7877 for (i = 0; i < NUM_DIRECTIONS; i++)
7879 int move_dir = test_dir[start_test + i];
7880 int move_dir_preference;
7882 xx = x + test_xy[start_test + i][0];
7883 yy = y + test_xy[start_test + i][1];
7885 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7886 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7888 new_move_dir = move_dir;
7893 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7896 move_dir_preference = -1 * RunnerVisit[xx][yy];
7897 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7898 move_dir_preference = PlayerVisit[xx][yy];
7900 if (move_dir_preference > move_preference)
7902 /* prefer field that has not been visited for the longest time */
7903 move_preference = move_dir_preference;
7904 new_move_dir = move_dir;
7906 else if (move_dir_preference == move_preference &&
7907 move_dir == old_move_dir)
7909 /* prefer last direction when all directions are preferred equally */
7910 move_preference = move_dir_preference;
7911 new_move_dir = move_dir;
7915 MovDir[x][y] = new_move_dir;
7916 if (old_move_dir != new_move_dir)
7917 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7921 static void TurnRound(int x, int y)
7923 int direction = MovDir[x][y];
7927 GfxDir[x][y] = MovDir[x][y];
7929 if (direction != MovDir[x][y])
7933 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7935 ResetGfxFrame(x, y, FALSE);
7938 static boolean JustBeingPushed(int x, int y)
7942 for (i = 0; i < MAX_PLAYERS; i++)
7944 struct PlayerInfo *player = &stored_player[i];
7946 if (player->active && player->is_pushing && player->MovPos)
7948 int next_jx = player->jx + (player->jx - player->last_jx);
7949 int next_jy = player->jy + (player->jy - player->last_jy);
7951 if (x == next_jx && y == next_jy)
7959 void StartMoving(int x, int y)
7961 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7962 int element = Feld[x][y];
7967 if (MovDelay[x][y] == 0)
7968 GfxAction[x][y] = ACTION_DEFAULT;
7970 if (CAN_FALL(element) && y < lev_fieldy - 1)
7972 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7973 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7974 if (JustBeingPushed(x, y))
7977 if (element == EL_QUICKSAND_FULL)
7979 if (IS_FREE(x, y + 1))
7981 InitMovingField(x, y, MV_DOWN);
7982 started_moving = TRUE;
7984 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7985 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7986 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7987 Store[x][y] = EL_ROCK;
7989 Store[x][y] = EL_ROCK;
7992 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7994 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7996 if (!MovDelay[x][y])
7998 MovDelay[x][y] = TILEY + 1;
8000 ResetGfxAnimation(x, y);
8001 ResetGfxAnimation(x, y + 1);
8006 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8007 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8014 Feld[x][y] = EL_QUICKSAND_EMPTY;
8015 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8016 Store[x][y + 1] = Store[x][y];
8019 PlayLevelSoundAction(x, y, ACTION_FILLING);
8021 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8023 if (!MovDelay[x][y])
8025 MovDelay[x][y] = TILEY + 1;
8027 ResetGfxAnimation(x, y);
8028 ResetGfxAnimation(x, y + 1);
8033 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8034 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8041 Feld[x][y] = EL_QUICKSAND_EMPTY;
8042 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8043 Store[x][y + 1] = Store[x][y];
8046 PlayLevelSoundAction(x, y, ACTION_FILLING);
8049 else if (element == EL_QUICKSAND_FAST_FULL)
8051 if (IS_FREE(x, y + 1))
8053 InitMovingField(x, y, MV_DOWN);
8054 started_moving = TRUE;
8056 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8057 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8058 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8059 Store[x][y] = EL_ROCK;
8061 Store[x][y] = EL_ROCK;
8064 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
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_FAST_EMPTYING);
8079 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8086 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8087 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8088 Store[x][y + 1] = Store[x][y];
8091 PlayLevelSoundAction(x, y, ACTION_FILLING);
8093 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8095 if (!MovDelay[x][y])
8097 MovDelay[x][y] = TILEY + 1;
8099 ResetGfxAnimation(x, y);
8100 ResetGfxAnimation(x, y + 1);
8105 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8106 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8113 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8114 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8115 Store[x][y + 1] = Store[x][y];
8118 PlayLevelSoundAction(x, y, ACTION_FILLING);
8121 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8122 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8124 InitMovingField(x, y, MV_DOWN);
8125 started_moving = TRUE;
8127 Feld[x][y] = EL_QUICKSAND_FILLING;
8128 Store[x][y] = element;
8130 PlayLevelSoundAction(x, y, ACTION_FILLING);
8132 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8133 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8135 InitMovingField(x, y, MV_DOWN);
8136 started_moving = TRUE;
8138 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8139 Store[x][y] = element;
8141 PlayLevelSoundAction(x, y, ACTION_FILLING);
8143 else if (element == EL_MAGIC_WALL_FULL)
8145 if (IS_FREE(x, y + 1))
8147 InitMovingField(x, y, MV_DOWN);
8148 started_moving = TRUE;
8150 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8151 Store[x][y] = EL_CHANGED(Store[x][y]);
8153 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8155 if (!MovDelay[x][y])
8156 MovDelay[x][y] = TILEY / 4 + 1;
8165 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8166 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8167 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8171 else if (element == EL_BD_MAGIC_WALL_FULL)
8173 if (IS_FREE(x, y + 1))
8175 InitMovingField(x, y, MV_DOWN);
8176 started_moving = TRUE;
8178 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8179 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8181 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8183 if (!MovDelay[x][y])
8184 MovDelay[x][y] = TILEY / 4 + 1;
8193 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8194 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8195 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8199 else if (element == EL_DC_MAGIC_WALL_FULL)
8201 if (IS_FREE(x, y + 1))
8203 InitMovingField(x, y, MV_DOWN);
8204 started_moving = TRUE;
8206 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8207 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8209 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8211 if (!MovDelay[x][y])
8212 MovDelay[x][y] = TILEY / 4 + 1;
8221 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8222 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8223 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8227 else if ((CAN_PASS_MAGIC_WALL(element) &&
8228 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8229 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8230 (CAN_PASS_DC_MAGIC_WALL(element) &&
8231 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8234 InitMovingField(x, y, MV_DOWN);
8235 started_moving = TRUE;
8238 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8239 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8240 EL_DC_MAGIC_WALL_FILLING);
8241 Store[x][y] = element;
8243 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8245 SplashAcid(x, y + 1);
8247 InitMovingField(x, y, MV_DOWN);
8248 started_moving = TRUE;
8250 Store[x][y] = EL_ACID;
8253 #if USE_FIX_IMPACT_COLLISION
8254 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8255 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8257 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8258 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8260 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8261 CAN_FALL(element) && WasJustFalling[x][y] &&
8262 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8264 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8265 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8266 (Feld[x][y + 1] == EL_BLOCKED)))
8268 /* this is needed for a special case not covered by calling "Impact()"
8269 from "ContinueMoving()": if an element moves to a tile directly below
8270 another element which was just falling on that tile (which was empty
8271 in the previous frame), the falling element above would just stop
8272 instead of smashing the element below (in previous version, the above
8273 element was just checked for "moving" instead of "falling", resulting
8274 in incorrect smashes caused by horizontal movement of the above
8275 element; also, the case of the player being the element to smash was
8276 simply not covered here... :-/ ) */
8278 CheckCollision[x][y] = 0;
8279 CheckImpact[x][y] = 0;
8283 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8285 if (MovDir[x][y] == MV_NONE)
8287 InitMovingField(x, y, MV_DOWN);
8288 started_moving = TRUE;
8291 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8293 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8294 MovDir[x][y] = MV_DOWN;
8296 InitMovingField(x, y, MV_DOWN);
8297 started_moving = TRUE;
8299 else if (element == EL_AMOEBA_DROP)
8301 Feld[x][y] = EL_AMOEBA_GROWING;
8302 Store[x][y] = EL_AMOEBA_WET;
8304 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8305 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8306 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8307 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8309 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8310 (IS_FREE(x - 1, y + 1) ||
8311 Feld[x - 1][y + 1] == EL_ACID));
8312 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8313 (IS_FREE(x + 1, y + 1) ||
8314 Feld[x + 1][y + 1] == EL_ACID));
8315 boolean can_fall_any = (can_fall_left || can_fall_right);
8316 boolean can_fall_both = (can_fall_left && can_fall_right);
8317 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8319 #if USE_NEW_ALL_SLIPPERY
8320 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8322 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8323 can_fall_right = FALSE;
8324 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8325 can_fall_left = FALSE;
8326 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8327 can_fall_right = FALSE;
8328 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8329 can_fall_left = FALSE;
8331 can_fall_any = (can_fall_left || can_fall_right);
8332 can_fall_both = FALSE;
8335 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8337 if (slippery_type == SLIPPERY_ONLY_LEFT)
8338 can_fall_right = FALSE;
8339 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8340 can_fall_left = FALSE;
8341 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8342 can_fall_right = FALSE;
8343 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8344 can_fall_left = FALSE;
8346 can_fall_any = (can_fall_left || can_fall_right);
8347 can_fall_both = (can_fall_left && can_fall_right);
8351 #if USE_NEW_ALL_SLIPPERY
8353 #if USE_NEW_SP_SLIPPERY
8354 /* !!! better use the same properties as for custom elements here !!! */
8355 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8356 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8358 can_fall_right = FALSE; /* slip down on left side */
8359 can_fall_both = FALSE;
8364 #if USE_NEW_ALL_SLIPPERY
8367 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8368 can_fall_right = FALSE; /* slip down on left side */
8370 can_fall_left = !(can_fall_right = RND(2));
8372 can_fall_both = FALSE;
8377 if (game.emulation == EMU_BOULDERDASH ||
8378 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8379 can_fall_right = FALSE; /* slip down on left side */
8381 can_fall_left = !(can_fall_right = RND(2));
8383 can_fall_both = FALSE;
8389 /* if not determined otherwise, prefer left side for slipping down */
8390 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8391 started_moving = TRUE;
8395 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8397 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8400 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8401 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8402 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8403 int belt_dir = game.belt_dir[belt_nr];
8405 if ((belt_dir == MV_LEFT && left_is_free) ||
8406 (belt_dir == MV_RIGHT && right_is_free))
8408 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8410 InitMovingField(x, y, belt_dir);
8411 started_moving = TRUE;
8413 Pushed[x][y] = TRUE;
8414 Pushed[nextx][y] = TRUE;
8416 GfxAction[x][y] = ACTION_DEFAULT;
8420 MovDir[x][y] = 0; /* if element was moving, stop it */
8425 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8427 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8429 if (CAN_MOVE(element) && !started_moving)
8432 int move_pattern = element_info[element].move_pattern;
8437 if (MovDir[x][y] == MV_NONE)
8439 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8440 x, y, element, element_info[element].token_name);
8441 printf("StartMoving(): This should never happen!\n");
8446 Moving2Blocked(x, y, &newx, &newy);
8448 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8451 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8452 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8454 WasJustMoving[x][y] = 0;
8455 CheckCollision[x][y] = 0;
8457 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8459 if (Feld[x][y] != element) /* element has changed */
8463 if (!MovDelay[x][y]) /* start new movement phase */
8465 /* all objects that can change their move direction after each step
8466 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8468 if (element != EL_YAMYAM &&
8469 element != EL_DARK_YAMYAM &&
8470 element != EL_PACMAN &&
8471 !(move_pattern & MV_ANY_DIRECTION) &&
8472 move_pattern != MV_TURNING_LEFT &&
8473 move_pattern != MV_TURNING_RIGHT &&
8474 move_pattern != MV_TURNING_LEFT_RIGHT &&
8475 move_pattern != MV_TURNING_RIGHT_LEFT &&
8476 move_pattern != MV_TURNING_RANDOM)
8480 if (MovDelay[x][y] && (element == EL_BUG ||
8481 element == EL_SPACESHIP ||
8482 element == EL_SP_SNIKSNAK ||
8483 element == EL_SP_ELECTRON ||
8484 element == EL_MOLE))
8485 TEST_DrawLevelField(x, y);
8489 if (MovDelay[x][y]) /* wait some time before next movement */
8493 if (element == EL_ROBOT ||
8494 element == EL_YAMYAM ||
8495 element == EL_DARK_YAMYAM)
8497 DrawLevelElementAnimationIfNeeded(x, y, element);
8498 PlayLevelSoundAction(x, y, ACTION_WAITING);
8500 else if (element == EL_SP_ELECTRON)
8501 DrawLevelElementAnimationIfNeeded(x, y, element);
8502 else if (element == EL_DRAGON)
8505 int dir = MovDir[x][y];
8506 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8507 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8508 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8509 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8510 dir == MV_UP ? IMG_FLAMES_1_UP :
8511 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8512 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8514 GfxAction[x][y] = ACTION_ATTACKING;
8516 if (IS_PLAYER(x, y))
8517 DrawPlayerField(x, y);
8519 TEST_DrawLevelField(x, y);
8521 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8523 for (i = 1; i <= 3; i++)
8525 int xx = x + i * dx;
8526 int yy = y + i * dy;
8527 int sx = SCREENX(xx);
8528 int sy = SCREENY(yy);
8529 int flame_graphic = graphic + (i - 1);
8531 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8536 int flamed = MovingOrBlocked2Element(xx, yy);
8540 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8542 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8543 RemoveMovingField(xx, yy);
8545 RemoveField(xx, yy);
8547 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8550 RemoveMovingField(xx, yy);
8553 ChangeDelay[xx][yy] = 0;
8555 Feld[xx][yy] = EL_FLAMES;
8557 if (IN_SCR_FIELD(sx, sy))
8559 TEST_DrawLevelFieldCrumbled(xx, yy);
8560 DrawGraphic(sx, sy, flame_graphic, frame);
8565 if (Feld[xx][yy] == EL_FLAMES)
8566 Feld[xx][yy] = EL_EMPTY;
8567 TEST_DrawLevelField(xx, yy);
8572 if (MovDelay[x][y]) /* element still has to wait some time */
8574 PlayLevelSoundAction(x, y, ACTION_WAITING);
8580 /* now make next step */
8582 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8584 if (DONT_COLLIDE_WITH(element) &&
8585 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8586 !PLAYER_ENEMY_PROTECTED(newx, newy))
8588 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8593 else if (CAN_MOVE_INTO_ACID(element) &&
8594 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8595 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8596 (MovDir[x][y] == MV_DOWN ||
8597 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8599 SplashAcid(newx, newy);
8600 Store[x][y] = EL_ACID;
8602 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8604 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8605 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8606 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8607 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8610 TEST_DrawLevelField(x, y);
8612 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8613 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8614 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8616 local_player->friends_still_needed--;
8617 if (!local_player->friends_still_needed &&
8618 !local_player->GameOver && AllPlayersGone)
8619 PlayerWins(local_player);
8623 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8625 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8626 TEST_DrawLevelField(newx, newy);
8628 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8630 else if (!IS_FREE(newx, newy))
8632 GfxAction[x][y] = ACTION_WAITING;
8634 if (IS_PLAYER(x, y))
8635 DrawPlayerField(x, y);
8637 TEST_DrawLevelField(x, y);
8642 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8644 if (IS_FOOD_PIG(Feld[newx][newy]))
8646 if (IS_MOVING(newx, newy))
8647 RemoveMovingField(newx, newy);
8650 Feld[newx][newy] = EL_EMPTY;
8651 TEST_DrawLevelField(newx, newy);
8654 PlayLevelSound(x, y, SND_PIG_DIGGING);
8656 else if (!IS_FREE(newx, newy))
8658 if (IS_PLAYER(x, y))
8659 DrawPlayerField(x, y);
8661 TEST_DrawLevelField(x, y);
8666 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8668 if (Store[x][y] != EL_EMPTY)
8670 boolean can_clone = FALSE;
8673 /* check if element to clone is still there */
8674 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8676 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8684 /* cannot clone or target field not free anymore -- do not clone */
8685 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8686 Store[x][y] = EL_EMPTY;
8689 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8691 if (IS_MV_DIAGONAL(MovDir[x][y]))
8693 int diagonal_move_dir = MovDir[x][y];
8694 int stored = Store[x][y];
8695 int change_delay = 8;
8698 /* android is moving diagonally */
8700 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8702 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8703 GfxElement[x][y] = EL_EMC_ANDROID;
8704 GfxAction[x][y] = ACTION_SHRINKING;
8705 GfxDir[x][y] = diagonal_move_dir;
8706 ChangeDelay[x][y] = change_delay;
8708 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8711 DrawLevelGraphicAnimation(x, y, graphic);
8712 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8714 if (Feld[newx][newy] == EL_ACID)
8716 SplashAcid(newx, newy);
8721 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8723 Store[newx][newy] = EL_EMC_ANDROID;
8724 GfxElement[newx][newy] = EL_EMC_ANDROID;
8725 GfxAction[newx][newy] = ACTION_GROWING;
8726 GfxDir[newx][newy] = diagonal_move_dir;
8727 ChangeDelay[newx][newy] = change_delay;
8729 graphic = el_act_dir2img(GfxElement[newx][newy],
8730 GfxAction[newx][newy], GfxDir[newx][newy]);
8732 DrawLevelGraphicAnimation(newx, newy, graphic);
8733 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8739 Feld[newx][newy] = EL_EMPTY;
8740 TEST_DrawLevelField(newx, newy);
8742 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8745 else if (!IS_FREE(newx, newy))
8748 if (IS_PLAYER(x, y))
8749 DrawPlayerField(x, y);
8751 TEST_DrawLevelField(x, y);
8757 else if (IS_CUSTOM_ELEMENT(element) &&
8758 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8761 if (!DigFieldByCE(newx, newy, element))
8764 int new_element = Feld[newx][newy];
8766 if (!IS_FREE(newx, newy))
8768 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8769 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8772 /* no element can dig solid indestructible elements */
8773 if (IS_INDESTRUCTIBLE(new_element) &&
8774 !IS_DIGGABLE(new_element) &&
8775 !IS_COLLECTIBLE(new_element))
8778 if (AmoebaNr[newx][newy] &&
8779 (new_element == EL_AMOEBA_FULL ||
8780 new_element == EL_BD_AMOEBA ||
8781 new_element == EL_AMOEBA_GROWING))
8783 AmoebaCnt[AmoebaNr[newx][newy]]--;
8784 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8787 if (IS_MOVING(newx, newy))
8788 RemoveMovingField(newx, newy);
8791 RemoveField(newx, newy);
8792 TEST_DrawLevelField(newx, newy);
8795 /* if digged element was about to explode, prevent the explosion */
8796 ExplodeField[newx][newy] = EX_TYPE_NONE;
8798 PlayLevelSoundAction(x, y, action);
8801 Store[newx][newy] = EL_EMPTY;
8804 /* this makes it possible to leave the removed element again */
8805 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8806 Store[newx][newy] = new_element;
8808 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8810 int move_leave_element = element_info[element].move_leave_element;
8812 /* this makes it possible to leave the removed element again */
8813 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8814 new_element : move_leave_element);
8820 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8822 RunnerVisit[x][y] = FrameCounter;
8823 PlayerVisit[x][y] /= 8; /* expire player visit path */
8826 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8828 if (!IS_FREE(newx, newy))
8830 if (IS_PLAYER(x, y))
8831 DrawPlayerField(x, y);
8833 TEST_DrawLevelField(x, y);
8839 boolean wanna_flame = !RND(10);
8840 int dx = newx - x, dy = newy - y;
8841 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8842 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8843 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8844 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8845 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8846 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8849 IS_CLASSIC_ENEMY(element1) ||
8850 IS_CLASSIC_ENEMY(element2)) &&
8851 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8852 element1 != EL_FLAMES && element2 != EL_FLAMES)
8854 ResetGfxAnimation(x, y);
8855 GfxAction[x][y] = ACTION_ATTACKING;
8857 if (IS_PLAYER(x, y))
8858 DrawPlayerField(x, y);
8860 TEST_DrawLevelField(x, y);
8862 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8864 MovDelay[x][y] = 50;
8868 RemoveField(newx, newy);
8870 Feld[newx][newy] = EL_FLAMES;
8871 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8874 RemoveField(newx1, newy1);
8876 Feld[newx1][newy1] = EL_FLAMES;
8878 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8881 RemoveField(newx2, newy2);
8883 Feld[newx2][newy2] = EL_FLAMES;
8890 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8891 Feld[newx][newy] == EL_DIAMOND)
8893 if (IS_MOVING(newx, newy))
8894 RemoveMovingField(newx, newy);
8897 Feld[newx][newy] = EL_EMPTY;
8898 TEST_DrawLevelField(newx, newy);
8901 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8903 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8904 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8906 if (AmoebaNr[newx][newy])
8908 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8909 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8910 Feld[newx][newy] == EL_BD_AMOEBA)
8911 AmoebaCnt[AmoebaNr[newx][newy]]--;
8916 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8918 RemoveMovingField(newx, newy);
8921 if (IS_MOVING(newx, newy))
8923 RemoveMovingField(newx, newy);
8928 Feld[newx][newy] = EL_EMPTY;
8929 TEST_DrawLevelField(newx, newy);
8932 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8934 else if ((element == EL_PACMAN || element == EL_MOLE)
8935 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8937 if (AmoebaNr[newx][newy])
8939 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8940 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8941 Feld[newx][newy] == EL_BD_AMOEBA)
8942 AmoebaCnt[AmoebaNr[newx][newy]]--;
8945 if (element == EL_MOLE)
8947 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8948 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8950 ResetGfxAnimation(x, y);
8951 GfxAction[x][y] = ACTION_DIGGING;
8952 TEST_DrawLevelField(x, y);
8954 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8956 return; /* wait for shrinking amoeba */
8958 else /* element == EL_PACMAN */
8960 Feld[newx][newy] = EL_EMPTY;
8961 TEST_DrawLevelField(newx, newy);
8962 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8965 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8966 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8967 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8969 /* wait for shrinking amoeba to completely disappear */
8972 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8974 /* object was running against a wall */
8979 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8980 if (move_pattern & MV_ANY_DIRECTION &&
8981 move_pattern == MovDir[x][y])
8983 int blocking_element =
8984 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8986 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8989 element = Feld[x][y]; /* element might have changed */
8993 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8994 DrawLevelElementAnimation(x, y, element);
8996 if (DONT_TOUCH(element))
8997 TestIfBadThingTouchesPlayer(x, y);
9002 InitMovingField(x, y, MovDir[x][y]);
9004 PlayLevelSoundAction(x, y, ACTION_MOVING);
9008 ContinueMoving(x, y);
9011 void ContinueMoving(int x, int y)
9013 int element = Feld[x][y];
9014 struct ElementInfo *ei = &element_info[element];
9015 int direction = MovDir[x][y];
9016 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9017 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
9018 int newx = x + dx, newy = y + dy;
9019 int stored = Store[x][y];
9020 int stored_new = Store[newx][newy];
9021 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
9022 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
9023 boolean last_line = (newy == lev_fieldy - 1);
9025 MovPos[x][y] += getElementMoveStepsize(x, y);
9027 if (pushed_by_player) /* special case: moving object pushed by player */
9028 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
9030 if (ABS(MovPos[x][y]) < TILEX)
9033 int ee = Feld[x][y];
9034 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9035 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
9037 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9038 x, y, ABS(MovPos[x][y]),
9040 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9043 TEST_DrawLevelField(x, y);
9045 return; /* element is still moving */
9048 /* element reached destination field */
9050 Feld[x][y] = EL_EMPTY;
9051 Feld[newx][newy] = element;
9052 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
9054 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9056 element = Feld[newx][newy] = EL_ACID;
9058 else if (element == EL_MOLE)
9060 Feld[x][y] = EL_SAND;
9062 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9064 else if (element == EL_QUICKSAND_FILLING)
9066 element = Feld[newx][newy] = get_next_element(element);
9067 Store[newx][newy] = Store[x][y];
9069 else if (element == EL_QUICKSAND_EMPTYING)
9071 Feld[x][y] = get_next_element(element);
9072 element = Feld[newx][newy] = Store[x][y];
9074 else if (element == EL_QUICKSAND_FAST_FILLING)
9076 element = Feld[newx][newy] = get_next_element(element);
9077 Store[newx][newy] = Store[x][y];
9079 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9081 Feld[x][y] = get_next_element(element);
9082 element = Feld[newx][newy] = Store[x][y];
9084 else if (element == EL_MAGIC_WALL_FILLING)
9086 element = Feld[newx][newy] = get_next_element(element);
9087 if (!game.magic_wall_active)
9088 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9089 Store[newx][newy] = Store[x][y];
9091 else if (element == EL_MAGIC_WALL_EMPTYING)
9093 Feld[x][y] = get_next_element(element);
9094 if (!game.magic_wall_active)
9095 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9096 element = Feld[newx][newy] = Store[x][y];
9098 #if USE_NEW_CUSTOM_VALUE
9099 InitField(newx, newy, FALSE);
9102 else if (element == EL_BD_MAGIC_WALL_FILLING)
9104 element = Feld[newx][newy] = get_next_element(element);
9105 if (!game.magic_wall_active)
9106 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9107 Store[newx][newy] = Store[x][y];
9109 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9111 Feld[x][y] = get_next_element(element);
9112 if (!game.magic_wall_active)
9113 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9114 element = Feld[newx][newy] = Store[x][y];
9116 #if USE_NEW_CUSTOM_VALUE
9117 InitField(newx, newy, FALSE);
9120 else if (element == EL_DC_MAGIC_WALL_FILLING)
9122 element = Feld[newx][newy] = get_next_element(element);
9123 if (!game.magic_wall_active)
9124 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9125 Store[newx][newy] = Store[x][y];
9127 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9129 Feld[x][y] = get_next_element(element);
9130 if (!game.magic_wall_active)
9131 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9132 element = Feld[newx][newy] = Store[x][y];
9134 #if USE_NEW_CUSTOM_VALUE
9135 InitField(newx, newy, FALSE);
9138 else if (element == EL_AMOEBA_DROPPING)
9140 Feld[x][y] = get_next_element(element);
9141 element = Feld[newx][newy] = Store[x][y];
9143 else if (element == EL_SOKOBAN_OBJECT)
9146 Feld[x][y] = Back[x][y];
9148 if (Back[newx][newy])
9149 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9151 Back[x][y] = Back[newx][newy] = 0;
9154 Store[x][y] = EL_EMPTY;
9159 MovDelay[newx][newy] = 0;
9161 if (CAN_CHANGE_OR_HAS_ACTION(element))
9163 /* copy element change control values to new field */
9164 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9165 ChangePage[newx][newy] = ChangePage[x][y];
9166 ChangeCount[newx][newy] = ChangeCount[x][y];
9167 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9170 #if USE_NEW_CUSTOM_VALUE
9171 CustomValue[newx][newy] = CustomValue[x][y];
9174 ChangeDelay[x][y] = 0;
9175 ChangePage[x][y] = -1;
9176 ChangeCount[x][y] = 0;
9177 ChangeEvent[x][y] = -1;
9179 #if USE_NEW_CUSTOM_VALUE
9180 CustomValue[x][y] = 0;
9183 /* copy animation control values to new field */
9184 GfxFrame[newx][newy] = GfxFrame[x][y];
9185 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9186 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9187 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9189 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9191 /* some elements can leave other elements behind after moving */
9193 if (ei->move_leave_element != EL_EMPTY &&
9194 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9195 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9197 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9198 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9199 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9202 int move_leave_element = ei->move_leave_element;
9206 /* this makes it possible to leave the removed element again */
9207 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9208 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9210 /* this makes it possible to leave the removed element again */
9211 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9212 move_leave_element = stored;
9215 /* this makes it possible to leave the removed element again */
9216 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9217 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9218 move_leave_element = stored;
9221 Feld[x][y] = move_leave_element;
9223 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9224 MovDir[x][y] = direction;
9226 InitField(x, y, FALSE);
9228 if (GFX_CRUMBLED(Feld[x][y]))
9229 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9231 if (ELEM_IS_PLAYER(move_leave_element))
9232 RelocatePlayer(x, y, move_leave_element);
9235 /* do this after checking for left-behind element */
9236 ResetGfxAnimation(x, y); /* reset animation values for old field */
9238 if (!CAN_MOVE(element) ||
9239 (CAN_FALL(element) && direction == MV_DOWN &&
9240 (element == EL_SPRING ||
9241 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9242 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9243 GfxDir[x][y] = MovDir[newx][newy] = 0;
9245 TEST_DrawLevelField(x, y);
9246 TEST_DrawLevelField(newx, newy);
9248 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9250 /* prevent pushed element from moving on in pushed direction */
9251 if (pushed_by_player && CAN_MOVE(element) &&
9252 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9253 !(element_info[element].move_pattern & direction))
9254 TurnRound(newx, newy);
9256 /* prevent elements on conveyor belt from moving on in last direction */
9257 if (pushed_by_conveyor && CAN_FALL(element) &&
9258 direction & MV_HORIZONTAL)
9259 MovDir[newx][newy] = 0;
9261 if (!pushed_by_player)
9263 int nextx = newx + dx, nexty = newy + dy;
9264 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9266 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9268 if (CAN_FALL(element) && direction == MV_DOWN)
9269 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9271 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9272 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9274 #if USE_FIX_IMPACT_COLLISION
9275 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9276 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9280 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9282 TestIfBadThingTouchesPlayer(newx, newy);
9283 TestIfBadThingTouchesFriend(newx, newy);
9285 if (!IS_CUSTOM_ELEMENT(element))
9286 TestIfBadThingTouchesOtherBadThing(newx, newy);
9288 else if (element == EL_PENGUIN)
9289 TestIfFriendTouchesBadThing(newx, newy);
9291 if (DONT_GET_HIT_BY(element))
9293 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9296 /* give the player one last chance (one more frame) to move away */
9297 if (CAN_FALL(element) && direction == MV_DOWN &&
9298 (last_line || (!IS_FREE(x, newy + 1) &&
9299 (!IS_PLAYER(x, newy + 1) ||
9300 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9303 if (pushed_by_player && !game.use_change_when_pushing_bug)
9305 int push_side = MV_DIR_OPPOSITE(direction);
9306 struct PlayerInfo *player = PLAYERINFO(x, y);
9308 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9309 player->index_bit, push_side);
9310 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9311 player->index_bit, push_side);
9314 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9315 MovDelay[newx][newy] = 1;
9317 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9319 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9322 if (ChangePage[newx][newy] != -1) /* delayed change */
9324 int page = ChangePage[newx][newy];
9325 struct ElementChangeInfo *change = &ei->change_page[page];
9327 ChangePage[newx][newy] = -1;
9329 if (change->can_change)
9331 if (ChangeElement(newx, newy, element, page))
9333 if (change->post_change_function)
9334 change->post_change_function(newx, newy);
9338 if (change->has_action)
9339 ExecuteCustomElementAction(newx, newy, element, page);
9343 TestIfElementHitsCustomElement(newx, newy, direction);
9344 TestIfPlayerTouchesCustomElement(newx, newy);
9345 TestIfElementTouchesCustomElement(newx, newy);
9347 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9348 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9349 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9350 MV_DIR_OPPOSITE(direction));
9353 int AmoebeNachbarNr(int ax, int ay)
9356 int element = Feld[ax][ay];
9358 static int xy[4][2] =
9366 for (i = 0; i < NUM_DIRECTIONS; i++)
9368 int x = ax + xy[i][0];
9369 int y = ay + xy[i][1];
9371 if (!IN_LEV_FIELD(x, y))
9374 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9375 group_nr = AmoebaNr[x][y];
9381 void AmoebenVereinigen(int ax, int ay)
9383 int i, x, y, xx, yy;
9384 int new_group_nr = AmoebaNr[ax][ay];
9385 static int xy[4][2] =
9393 if (new_group_nr == 0)
9396 for (i = 0; i < NUM_DIRECTIONS; i++)
9401 if (!IN_LEV_FIELD(x, y))
9404 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9405 Feld[x][y] == EL_BD_AMOEBA ||
9406 Feld[x][y] == EL_AMOEBA_DEAD) &&
9407 AmoebaNr[x][y] != new_group_nr)
9409 int old_group_nr = AmoebaNr[x][y];
9411 if (old_group_nr == 0)
9414 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9415 AmoebaCnt[old_group_nr] = 0;
9416 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9417 AmoebaCnt2[old_group_nr] = 0;
9419 SCAN_PLAYFIELD(xx, yy)
9421 if (AmoebaNr[xx][yy] == old_group_nr)
9422 AmoebaNr[xx][yy] = new_group_nr;
9428 void AmoebeUmwandeln(int ax, int ay)
9432 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9434 int group_nr = AmoebaNr[ax][ay];
9439 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9440 printf("AmoebeUmwandeln(): This should never happen!\n");
9445 SCAN_PLAYFIELD(x, y)
9447 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9450 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9454 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9455 SND_AMOEBA_TURNING_TO_GEM :
9456 SND_AMOEBA_TURNING_TO_ROCK));
9461 static int xy[4][2] =
9469 for (i = 0; i < NUM_DIRECTIONS; i++)
9474 if (!IN_LEV_FIELD(x, y))
9477 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9479 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9480 SND_AMOEBA_TURNING_TO_GEM :
9481 SND_AMOEBA_TURNING_TO_ROCK));
9488 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9491 int group_nr = AmoebaNr[ax][ay];
9492 boolean done = FALSE;
9497 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9498 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9503 SCAN_PLAYFIELD(x, y)
9505 if (AmoebaNr[x][y] == group_nr &&
9506 (Feld[x][y] == EL_AMOEBA_DEAD ||
9507 Feld[x][y] == EL_BD_AMOEBA ||
9508 Feld[x][y] == EL_AMOEBA_GROWING))
9511 Feld[x][y] = new_element;
9512 InitField(x, y, FALSE);
9513 TEST_DrawLevelField(x, y);
9519 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9520 SND_BD_AMOEBA_TURNING_TO_ROCK :
9521 SND_BD_AMOEBA_TURNING_TO_GEM));
9524 void AmoebeWaechst(int x, int y)
9526 static unsigned long sound_delay = 0;
9527 static unsigned long sound_delay_value = 0;
9529 if (!MovDelay[x][y]) /* start new growing cycle */
9533 if (DelayReached(&sound_delay, sound_delay_value))
9535 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9536 sound_delay_value = 30;
9540 if (MovDelay[x][y]) /* wait some time before growing bigger */
9543 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9545 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9546 6 - MovDelay[x][y]);
9548 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9551 if (!MovDelay[x][y])
9553 Feld[x][y] = Store[x][y];
9555 TEST_DrawLevelField(x, y);
9560 void AmoebaDisappearing(int x, int y)
9562 static unsigned long sound_delay = 0;
9563 static unsigned long sound_delay_value = 0;
9565 if (!MovDelay[x][y]) /* start new shrinking cycle */
9569 if (DelayReached(&sound_delay, sound_delay_value))
9570 sound_delay_value = 30;
9573 if (MovDelay[x][y]) /* wait some time before shrinking */
9576 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9578 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9579 6 - MovDelay[x][y]);
9581 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9584 if (!MovDelay[x][y])
9586 Feld[x][y] = EL_EMPTY;
9587 TEST_DrawLevelField(x, y);
9589 /* don't let mole enter this field in this cycle;
9590 (give priority to objects falling to this field from above) */
9596 void AmoebeAbleger(int ax, int ay)
9599 int element = Feld[ax][ay];
9600 int graphic = el2img(element);
9601 int newax = ax, neway = ay;
9602 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9603 static int xy[4][2] =
9611 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9613 Feld[ax][ay] = EL_AMOEBA_DEAD;
9614 TEST_DrawLevelField(ax, ay);
9618 if (IS_ANIMATED(graphic))
9619 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9621 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9622 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9624 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9627 if (MovDelay[ax][ay])
9631 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9634 int x = ax + xy[start][0];
9635 int y = ay + xy[start][1];
9637 if (!IN_LEV_FIELD(x, y))
9640 if (IS_FREE(x, y) ||
9641 CAN_GROW_INTO(Feld[x][y]) ||
9642 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9643 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9649 if (newax == ax && neway == ay)
9652 else /* normal or "filled" (BD style) amoeba */
9655 boolean waiting_for_player = FALSE;
9657 for (i = 0; i < NUM_DIRECTIONS; i++)
9659 int j = (start + i) % 4;
9660 int x = ax + xy[j][0];
9661 int y = ay + xy[j][1];
9663 if (!IN_LEV_FIELD(x, y))
9666 if (IS_FREE(x, y) ||
9667 CAN_GROW_INTO(Feld[x][y]) ||
9668 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9669 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9675 else if (IS_PLAYER(x, y))
9676 waiting_for_player = TRUE;
9679 if (newax == ax && neway == ay) /* amoeba cannot grow */
9681 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9683 Feld[ax][ay] = EL_AMOEBA_DEAD;
9684 TEST_DrawLevelField(ax, ay);
9685 AmoebaCnt[AmoebaNr[ax][ay]]--;
9687 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9689 if (element == EL_AMOEBA_FULL)
9690 AmoebeUmwandeln(ax, ay);
9691 else if (element == EL_BD_AMOEBA)
9692 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9697 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9699 /* amoeba gets larger by growing in some direction */
9701 int new_group_nr = AmoebaNr[ax][ay];
9704 if (new_group_nr == 0)
9706 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9707 printf("AmoebeAbleger(): This should never happen!\n");
9712 AmoebaNr[newax][neway] = new_group_nr;
9713 AmoebaCnt[new_group_nr]++;
9714 AmoebaCnt2[new_group_nr]++;
9716 /* if amoeba touches other amoeba(s) after growing, unify them */
9717 AmoebenVereinigen(newax, neway);
9719 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9721 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9727 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9728 (neway == lev_fieldy - 1 && newax != ax))
9730 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9731 Store[newax][neway] = element;
9733 else if (neway == ay || element == EL_EMC_DRIPPER)
9735 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9737 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9741 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9742 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9743 Store[ax][ay] = EL_AMOEBA_DROP;
9744 ContinueMoving(ax, ay);
9748 TEST_DrawLevelField(newax, neway);
9751 void Life(int ax, int ay)
9755 int element = Feld[ax][ay];
9756 int graphic = el2img(element);
9757 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9759 boolean changed = FALSE;
9761 if (IS_ANIMATED(graphic))
9762 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9767 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9768 MovDelay[ax][ay] = life_time;
9770 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9773 if (MovDelay[ax][ay])
9777 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9779 int xx = ax+x1, yy = ay+y1;
9782 if (!IN_LEV_FIELD(xx, yy))
9785 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9787 int x = xx+x2, y = yy+y2;
9789 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9792 if (((Feld[x][y] == element ||
9793 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9795 (IS_FREE(x, y) && Stop[x][y]))
9799 if (xx == ax && yy == ay) /* field in the middle */
9801 if (nachbarn < life_parameter[0] ||
9802 nachbarn > life_parameter[1])
9804 Feld[xx][yy] = EL_EMPTY;
9806 TEST_DrawLevelField(xx, yy);
9807 Stop[xx][yy] = TRUE;
9811 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9812 { /* free border field */
9813 if (nachbarn >= life_parameter[2] &&
9814 nachbarn <= life_parameter[3])
9816 Feld[xx][yy] = element;
9817 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9819 TEST_DrawLevelField(xx, yy);
9820 Stop[xx][yy] = TRUE;
9827 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9828 SND_GAME_OF_LIFE_GROWING);
9831 static void InitRobotWheel(int x, int y)
9833 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9836 static void RunRobotWheel(int x, int y)
9838 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9841 static void StopRobotWheel(int x, int y)
9843 if (ZX == x && ZY == y)
9847 game.robot_wheel_active = FALSE;
9851 static void InitTimegateWheel(int x, int y)
9853 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9856 static void RunTimegateWheel(int x, int y)
9858 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9861 static void InitMagicBallDelay(int x, int y)
9864 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9866 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9870 static void ActivateMagicBall(int bx, int by)
9874 if (level.ball_random)
9876 int pos_border = RND(8); /* select one of the eight border elements */
9877 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9878 int xx = pos_content % 3;
9879 int yy = pos_content / 3;
9884 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9885 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9889 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9891 int xx = x - bx + 1;
9892 int yy = y - by + 1;
9894 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9895 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9899 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9902 void CheckExit(int x, int y)
9904 if (local_player->gems_still_needed > 0 ||
9905 local_player->sokobanfields_still_needed > 0 ||
9906 local_player->lights_still_needed > 0)
9908 int element = Feld[x][y];
9909 int graphic = el2img(element);
9911 if (IS_ANIMATED(graphic))
9912 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9917 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9920 Feld[x][y] = EL_EXIT_OPENING;
9922 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9925 void CheckExitEM(int x, int y)
9927 if (local_player->gems_still_needed > 0 ||
9928 local_player->sokobanfields_still_needed > 0 ||
9929 local_player->lights_still_needed > 0)
9931 int element = Feld[x][y];
9932 int graphic = el2img(element);
9934 if (IS_ANIMATED(graphic))
9935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9940 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9943 Feld[x][y] = EL_EM_EXIT_OPENING;
9945 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9948 void CheckExitSteel(int x, int y)
9950 if (local_player->gems_still_needed > 0 ||
9951 local_player->sokobanfields_still_needed > 0 ||
9952 local_player->lights_still_needed > 0)
9954 int element = Feld[x][y];
9955 int graphic = el2img(element);
9957 if (IS_ANIMATED(graphic))
9958 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9963 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9966 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9968 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9971 void CheckExitSteelEM(int x, int y)
9973 if (local_player->gems_still_needed > 0 ||
9974 local_player->sokobanfields_still_needed > 0 ||
9975 local_player->lights_still_needed > 0)
9977 int element = Feld[x][y];
9978 int graphic = el2img(element);
9980 if (IS_ANIMATED(graphic))
9981 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9986 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9989 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9991 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9994 void CheckExitSP(int x, int y)
9996 if (local_player->gems_still_needed > 0)
9998 int element = Feld[x][y];
9999 int graphic = el2img(element);
10001 if (IS_ANIMATED(graphic))
10002 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10007 if (AllPlayersGone) /* do not re-open exit door closed after last player */
10010 Feld[x][y] = EL_SP_EXIT_OPENING;
10012 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
10015 static void CloseAllOpenTimegates()
10019 SCAN_PLAYFIELD(x, y)
10021 int element = Feld[x][y];
10023 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
10025 Feld[x][y] = EL_TIMEGATE_CLOSING;
10027 PlayLevelSoundAction(x, y, ACTION_CLOSING);
10032 void DrawTwinkleOnField(int x, int y)
10034 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
10037 if (Feld[x][y] == EL_BD_DIAMOND)
10040 if (MovDelay[x][y] == 0) /* next animation frame */
10041 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10043 if (MovDelay[x][y] != 0) /* wait some time before next frame */
10047 DrawLevelElementAnimation(x, y, Feld[x][y]);
10049 if (MovDelay[x][y] != 0)
10051 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10052 10 - MovDelay[x][y]);
10054 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10059 void MauerWaechst(int x, int y)
10063 if (!MovDelay[x][y]) /* next animation frame */
10064 MovDelay[x][y] = 3 * delay;
10066 if (MovDelay[x][y]) /* wait some time before next frame */
10070 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10072 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10073 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10075 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10078 if (!MovDelay[x][y])
10080 if (MovDir[x][y] == MV_LEFT)
10082 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10083 TEST_DrawLevelField(x - 1, y);
10085 else if (MovDir[x][y] == MV_RIGHT)
10087 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10088 TEST_DrawLevelField(x + 1, y);
10090 else if (MovDir[x][y] == MV_UP)
10092 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10093 TEST_DrawLevelField(x, y - 1);
10097 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10098 TEST_DrawLevelField(x, y + 1);
10101 Feld[x][y] = Store[x][y];
10103 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10104 TEST_DrawLevelField(x, y);
10109 void MauerAbleger(int ax, int ay)
10111 int element = Feld[ax][ay];
10112 int graphic = el2img(element);
10113 boolean oben_frei = FALSE, unten_frei = FALSE;
10114 boolean links_frei = FALSE, rechts_frei = FALSE;
10115 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10116 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10117 boolean new_wall = FALSE;
10119 if (IS_ANIMATED(graphic))
10120 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10122 if (!MovDelay[ax][ay]) /* start building new wall */
10123 MovDelay[ax][ay] = 6;
10125 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10127 MovDelay[ax][ay]--;
10128 if (MovDelay[ax][ay])
10132 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10134 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10136 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10138 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10139 rechts_frei = TRUE;
10141 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10142 element == EL_EXPANDABLE_WALL_ANY)
10146 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10147 Store[ax][ay-1] = element;
10148 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10149 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10150 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10151 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10156 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10157 Store[ax][ay+1] = element;
10158 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10159 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10160 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10161 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10166 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10167 element == EL_EXPANDABLE_WALL_ANY ||
10168 element == EL_EXPANDABLE_WALL ||
10169 element == EL_BD_EXPANDABLE_WALL)
10173 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10174 Store[ax-1][ay] = element;
10175 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10176 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10177 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10178 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10184 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10185 Store[ax+1][ay] = element;
10186 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10187 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10188 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10189 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10194 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10195 TEST_DrawLevelField(ax, ay);
10197 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10198 oben_massiv = TRUE;
10199 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10200 unten_massiv = TRUE;
10201 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10202 links_massiv = TRUE;
10203 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10204 rechts_massiv = TRUE;
10206 if (((oben_massiv && unten_massiv) ||
10207 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10208 element == EL_EXPANDABLE_WALL) &&
10209 ((links_massiv && rechts_massiv) ||
10210 element == EL_EXPANDABLE_WALL_VERTICAL))
10211 Feld[ax][ay] = EL_WALL;
10214 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10217 void MauerAblegerStahl(int ax, int ay)
10219 int element = Feld[ax][ay];
10220 int graphic = el2img(element);
10221 boolean oben_frei = FALSE, unten_frei = FALSE;
10222 boolean links_frei = FALSE, rechts_frei = FALSE;
10223 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10224 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10225 boolean new_wall = FALSE;
10227 if (IS_ANIMATED(graphic))
10228 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10230 if (!MovDelay[ax][ay]) /* start building new wall */
10231 MovDelay[ax][ay] = 6;
10233 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10235 MovDelay[ax][ay]--;
10236 if (MovDelay[ax][ay])
10240 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10242 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10244 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10246 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10247 rechts_frei = TRUE;
10249 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10250 element == EL_EXPANDABLE_STEELWALL_ANY)
10254 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10255 Store[ax][ay-1] = element;
10256 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10257 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10258 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10259 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10264 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10265 Store[ax][ay+1] = element;
10266 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10267 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10268 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10269 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10274 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10275 element == EL_EXPANDABLE_STEELWALL_ANY)
10279 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10280 Store[ax-1][ay] = element;
10281 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10282 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10283 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10284 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10290 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10291 Store[ax+1][ay] = element;
10292 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10293 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10294 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10295 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10300 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10301 oben_massiv = TRUE;
10302 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10303 unten_massiv = TRUE;
10304 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10305 links_massiv = TRUE;
10306 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10307 rechts_massiv = TRUE;
10309 if (((oben_massiv && unten_massiv) ||
10310 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10311 ((links_massiv && rechts_massiv) ||
10312 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10313 Feld[ax][ay] = EL_STEELWALL;
10316 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10319 void CheckForDragon(int x, int y)
10322 boolean dragon_found = FALSE;
10323 static int xy[4][2] =
10331 for (i = 0; i < NUM_DIRECTIONS; i++)
10333 for (j = 0; j < 4; j++)
10335 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10337 if (IN_LEV_FIELD(xx, yy) &&
10338 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10340 if (Feld[xx][yy] == EL_DRAGON)
10341 dragon_found = TRUE;
10350 for (i = 0; i < NUM_DIRECTIONS; i++)
10352 for (j = 0; j < 3; j++)
10354 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10356 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10358 Feld[xx][yy] = EL_EMPTY;
10359 TEST_DrawLevelField(xx, yy);
10368 static void InitBuggyBase(int x, int y)
10370 int element = Feld[x][y];
10371 int activating_delay = FRAMES_PER_SECOND / 4;
10373 ChangeDelay[x][y] =
10374 (element == EL_SP_BUGGY_BASE ?
10375 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10376 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10378 element == EL_SP_BUGGY_BASE_ACTIVE ?
10379 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10382 static void WarnBuggyBase(int x, int y)
10385 static int xy[4][2] =
10393 for (i = 0; i < NUM_DIRECTIONS; i++)
10395 int xx = x + xy[i][0];
10396 int yy = y + xy[i][1];
10398 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10400 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10407 static void InitTrap(int x, int y)
10409 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10412 static void ActivateTrap(int x, int y)
10414 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10417 static void ChangeActiveTrap(int x, int y)
10419 int graphic = IMG_TRAP_ACTIVE;
10421 /* if new animation frame was drawn, correct crumbled sand border */
10422 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10423 TEST_DrawLevelFieldCrumbled(x, y);
10426 static int getSpecialActionElement(int element, int number, int base_element)
10428 return (element != EL_EMPTY ? element :
10429 number != -1 ? base_element + number - 1 :
10433 static int getModifiedActionNumber(int value_old, int operator, int operand,
10434 int value_min, int value_max)
10436 int value_new = (operator == CA_MODE_SET ? operand :
10437 operator == CA_MODE_ADD ? value_old + operand :
10438 operator == CA_MODE_SUBTRACT ? value_old - operand :
10439 operator == CA_MODE_MULTIPLY ? value_old * operand :
10440 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10441 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10444 return (value_new < value_min ? value_min :
10445 value_new > value_max ? value_max :
10449 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10451 struct ElementInfo *ei = &element_info[element];
10452 struct ElementChangeInfo *change = &ei->change_page[page];
10453 int target_element = change->target_element;
10454 int action_type = change->action_type;
10455 int action_mode = change->action_mode;
10456 int action_arg = change->action_arg;
10457 int action_element = change->action_element;
10460 if (!change->has_action)
10463 /* ---------- determine action paramater values -------------------------- */
10465 int level_time_value =
10466 (level.time > 0 ? TimeLeft :
10469 int action_arg_element_raw =
10470 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10471 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10472 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10473 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10474 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10475 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10476 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10478 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10481 if (action_arg_element_raw == EL_GROUP_START)
10482 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10485 int action_arg_direction =
10486 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10487 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10488 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10489 change->actual_trigger_side :
10490 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10491 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10494 int action_arg_number_min =
10495 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10498 int action_arg_number_max =
10499 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10500 action_type == CA_SET_LEVEL_GEMS ? 999 :
10501 action_type == CA_SET_LEVEL_TIME ? 9999 :
10502 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10503 action_type == CA_SET_CE_VALUE ? 9999 :
10504 action_type == CA_SET_CE_SCORE ? 9999 :
10507 int action_arg_number_reset =
10508 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10509 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10510 action_type == CA_SET_LEVEL_TIME ? level.time :
10511 action_type == CA_SET_LEVEL_SCORE ? 0 :
10512 #if USE_NEW_CUSTOM_VALUE
10513 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10515 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10517 action_type == CA_SET_CE_SCORE ? 0 :
10520 int action_arg_number =
10521 (action_arg <= CA_ARG_MAX ? action_arg :
10522 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10523 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10524 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10525 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10526 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10527 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10528 #if USE_NEW_CUSTOM_VALUE
10529 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10531 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10533 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10534 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10535 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10536 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10537 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10538 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10539 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10540 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10541 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10542 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10543 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10544 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10545 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10546 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10549 int action_arg_number_old =
10550 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10551 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10552 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10553 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10554 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10557 int action_arg_number_new =
10558 getModifiedActionNumber(action_arg_number_old,
10559 action_mode, action_arg_number,
10560 action_arg_number_min, action_arg_number_max);
10563 int trigger_player_bits =
10564 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10565 change->actual_trigger_player_bits : change->trigger_player);
10567 int trigger_player_bits =
10568 (change->actual_trigger_player >= EL_PLAYER_1 &&
10569 change->actual_trigger_player <= EL_PLAYER_4 ?
10570 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10574 int action_arg_player_bits =
10575 (action_arg >= CA_ARG_PLAYER_1 &&
10576 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10577 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10578 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10581 /* ---------- execute action -------------------------------------------- */
10583 switch (action_type)
10590 /* ---------- level actions ------------------------------------------- */
10592 case CA_RESTART_LEVEL:
10594 game.restart_level = TRUE;
10599 case CA_SHOW_ENVELOPE:
10601 int element = getSpecialActionElement(action_arg_element,
10602 action_arg_number, EL_ENVELOPE_1);
10604 if (IS_ENVELOPE(element))
10605 local_player->show_envelope = element;
10610 case CA_SET_LEVEL_TIME:
10612 if (level.time > 0) /* only modify limited time value */
10614 TimeLeft = action_arg_number_new;
10617 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10619 DisplayGameControlValues();
10621 DrawGameValue_Time(TimeLeft);
10624 if (!TimeLeft && setup.time_limit)
10625 for (i = 0; i < MAX_PLAYERS; i++)
10626 KillPlayer(&stored_player[i]);
10632 case CA_SET_LEVEL_SCORE:
10634 local_player->score = action_arg_number_new;
10637 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10639 DisplayGameControlValues();
10641 DrawGameValue_Score(local_player->score);
10647 case CA_SET_LEVEL_GEMS:
10649 local_player->gems_still_needed = action_arg_number_new;
10652 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10654 DisplayGameControlValues();
10656 DrawGameValue_Emeralds(local_player->gems_still_needed);
10662 #if !USE_PLAYER_GRAVITY
10663 case CA_SET_LEVEL_GRAVITY:
10665 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10666 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10667 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10673 case CA_SET_LEVEL_WIND:
10675 game.wind_direction = action_arg_direction;
10680 case CA_SET_LEVEL_RANDOM_SEED:
10683 /* ensure that setting a new random seed while playing is predictable */
10684 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10686 InitRND(action_arg_number_new);
10690 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10698 for (i = 0; i < 9; i++)
10699 printf("%d, ", RND(2));
10707 /* ---------- player actions ------------------------------------------ */
10709 case CA_MOVE_PLAYER:
10711 /* automatically move to the next field in specified direction */
10712 for (i = 0; i < MAX_PLAYERS; i++)
10713 if (trigger_player_bits & (1 << i))
10714 stored_player[i].programmed_action = action_arg_direction;
10719 case CA_EXIT_PLAYER:
10721 for (i = 0; i < MAX_PLAYERS; i++)
10722 if (action_arg_player_bits & (1 << i))
10723 PlayerWins(&stored_player[i]);
10728 case CA_KILL_PLAYER:
10730 for (i = 0; i < MAX_PLAYERS; i++)
10731 if (action_arg_player_bits & (1 << i))
10732 KillPlayer(&stored_player[i]);
10737 case CA_SET_PLAYER_KEYS:
10739 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10740 int element = getSpecialActionElement(action_arg_element,
10741 action_arg_number, EL_KEY_1);
10743 if (IS_KEY(element))
10745 for (i = 0; i < MAX_PLAYERS; i++)
10747 if (trigger_player_bits & (1 << i))
10749 stored_player[i].key[KEY_NR(element)] = key_state;
10751 DrawGameDoorValues();
10759 case CA_SET_PLAYER_SPEED:
10762 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10765 for (i = 0; i < MAX_PLAYERS; i++)
10767 if (trigger_player_bits & (1 << i))
10769 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10771 if (action_arg == CA_ARG_SPEED_FASTER &&
10772 stored_player[i].cannot_move)
10774 action_arg_number = STEPSIZE_VERY_SLOW;
10776 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10777 action_arg == CA_ARG_SPEED_FASTER)
10779 action_arg_number = 2;
10780 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10783 else if (action_arg == CA_ARG_NUMBER_RESET)
10785 action_arg_number = level.initial_player_stepsize[i];
10789 getModifiedActionNumber(move_stepsize,
10792 action_arg_number_min,
10793 action_arg_number_max);
10795 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10802 case CA_SET_PLAYER_SHIELD:
10804 for (i = 0; i < MAX_PLAYERS; i++)
10806 if (trigger_player_bits & (1 << i))
10808 if (action_arg == CA_ARG_SHIELD_OFF)
10810 stored_player[i].shield_normal_time_left = 0;
10811 stored_player[i].shield_deadly_time_left = 0;
10813 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10815 stored_player[i].shield_normal_time_left = 999999;
10817 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10819 stored_player[i].shield_normal_time_left = 999999;
10820 stored_player[i].shield_deadly_time_left = 999999;
10828 #if USE_PLAYER_GRAVITY
10829 case CA_SET_PLAYER_GRAVITY:
10831 for (i = 0; i < MAX_PLAYERS; i++)
10833 if (trigger_player_bits & (1 << i))
10835 stored_player[i].gravity =
10836 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10837 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10838 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10839 stored_player[i].gravity);
10847 case CA_SET_PLAYER_ARTWORK:
10849 for (i = 0; i < MAX_PLAYERS; i++)
10851 if (trigger_player_bits & (1 << i))
10853 int artwork_element = action_arg_element;
10855 if (action_arg == CA_ARG_ELEMENT_RESET)
10857 (level.use_artwork_element[i] ? level.artwork_element[i] :
10858 stored_player[i].element_nr);
10860 #if USE_GFX_RESET_PLAYER_ARTWORK
10861 if (stored_player[i].artwork_element != artwork_element)
10862 stored_player[i].Frame = 0;
10865 stored_player[i].artwork_element = artwork_element;
10867 SetPlayerWaiting(&stored_player[i], FALSE);
10869 /* set number of special actions for bored and sleeping animation */
10870 stored_player[i].num_special_action_bored =
10871 get_num_special_action(artwork_element,
10872 ACTION_BORING_1, ACTION_BORING_LAST);
10873 stored_player[i].num_special_action_sleeping =
10874 get_num_special_action(artwork_element,
10875 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10882 case CA_SET_PLAYER_INVENTORY:
10884 for (i = 0; i < MAX_PLAYERS; i++)
10886 struct PlayerInfo *player = &stored_player[i];
10889 if (trigger_player_bits & (1 << i))
10891 int inventory_element = action_arg_element;
10893 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10894 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10895 action_arg == CA_ARG_ELEMENT_ACTION)
10897 int element = inventory_element;
10898 int collect_count = element_info[element].collect_count_initial;
10900 if (!IS_CUSTOM_ELEMENT(element))
10903 if (collect_count == 0)
10904 player->inventory_infinite_element = element;
10906 for (k = 0; k < collect_count; k++)
10907 if (player->inventory_size < MAX_INVENTORY_SIZE)
10908 player->inventory_element[player->inventory_size++] =
10911 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10912 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10913 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10915 if (player->inventory_infinite_element != EL_UNDEFINED &&
10916 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10917 action_arg_element_raw))
10918 player->inventory_infinite_element = EL_UNDEFINED;
10920 for (k = 0, j = 0; j < player->inventory_size; j++)
10922 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10923 action_arg_element_raw))
10924 player->inventory_element[k++] = player->inventory_element[j];
10927 player->inventory_size = k;
10929 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10931 if (player->inventory_size > 0)
10933 for (j = 0; j < player->inventory_size - 1; j++)
10934 player->inventory_element[j] = player->inventory_element[j + 1];
10936 player->inventory_size--;
10939 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10941 if (player->inventory_size > 0)
10942 player->inventory_size--;
10944 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10946 player->inventory_infinite_element = EL_UNDEFINED;
10947 player->inventory_size = 0;
10949 else if (action_arg == CA_ARG_INVENTORY_RESET)
10951 player->inventory_infinite_element = EL_UNDEFINED;
10952 player->inventory_size = 0;
10954 if (level.use_initial_inventory[i])
10956 for (j = 0; j < level.initial_inventory_size[i]; j++)
10958 int element = level.initial_inventory_content[i][j];
10959 int collect_count = element_info[element].collect_count_initial;
10961 if (!IS_CUSTOM_ELEMENT(element))
10964 if (collect_count == 0)
10965 player->inventory_infinite_element = element;
10967 for (k = 0; k < collect_count; k++)
10968 if (player->inventory_size < MAX_INVENTORY_SIZE)
10969 player->inventory_element[player->inventory_size++] =
10980 /* ---------- CE actions ---------------------------------------------- */
10982 case CA_SET_CE_VALUE:
10984 #if USE_NEW_CUSTOM_VALUE
10985 int last_ce_value = CustomValue[x][y];
10987 CustomValue[x][y] = action_arg_number_new;
10989 if (CustomValue[x][y] != last_ce_value)
10991 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10992 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10994 if (CustomValue[x][y] == 0)
10996 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10997 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
11005 case CA_SET_CE_SCORE:
11007 #if USE_NEW_CUSTOM_VALUE
11008 int last_ce_score = ei->collect_score;
11010 ei->collect_score = action_arg_number_new;
11012 if (ei->collect_score != last_ce_score)
11014 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
11015 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
11017 if (ei->collect_score == 0)
11021 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
11022 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
11025 This is a very special case that seems to be a mixture between
11026 CheckElementChange() and CheckTriggeredElementChange(): while
11027 the first one only affects single elements that are triggered
11028 directly, the second one affects multiple elements in the playfield
11029 that are triggered indirectly by another element. This is a third
11030 case: Changing the CE score always affects multiple identical CEs,
11031 so every affected CE must be checked, not only the single CE for
11032 which the CE score was changed in the first place (as every instance
11033 of that CE shares the same CE score, and therefore also can change)!
11035 SCAN_PLAYFIELD(xx, yy)
11037 if (Feld[xx][yy] == element)
11038 CheckElementChange(xx, yy, element, EL_UNDEFINED,
11039 CE_SCORE_GETS_ZERO);
11048 case CA_SET_CE_ARTWORK:
11050 int artwork_element = action_arg_element;
11051 boolean reset_frame = FALSE;
11054 if (action_arg == CA_ARG_ELEMENT_RESET)
11055 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11058 if (ei->gfx_element != artwork_element)
11059 reset_frame = TRUE;
11061 ei->gfx_element = artwork_element;
11063 SCAN_PLAYFIELD(xx, yy)
11065 if (Feld[xx][yy] == element)
11069 ResetGfxAnimation(xx, yy);
11070 ResetRandomAnimationValue(xx, yy);
11073 TEST_DrawLevelField(xx, yy);
11080 /* ---------- engine actions ------------------------------------------ */
11082 case CA_SET_ENGINE_SCAN_MODE:
11084 InitPlayfieldScanMode(action_arg);
11094 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11096 int old_element = Feld[x][y];
11097 int new_element = GetElementFromGroupElement(element);
11098 int previous_move_direction = MovDir[x][y];
11099 #if USE_NEW_CUSTOM_VALUE
11100 int last_ce_value = CustomValue[x][y];
11102 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11103 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11104 boolean add_player_onto_element = (new_element_is_player &&
11105 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11106 /* this breaks SnakeBite when a snake is
11107 halfway through a door that closes */
11108 /* NOW FIXED AT LEVEL INIT IN files.c */
11109 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11111 IS_WALKABLE(old_element));
11114 /* check if element under the player changes from accessible to unaccessible
11115 (needed for special case of dropping element which then changes) */
11116 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11117 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11125 if (!add_player_onto_element)
11127 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11128 RemoveMovingField(x, y);
11132 Feld[x][y] = new_element;
11134 #if !USE_GFX_RESET_GFX_ANIMATION
11135 ResetGfxAnimation(x, y);
11136 ResetRandomAnimationValue(x, y);
11139 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11140 MovDir[x][y] = previous_move_direction;
11142 #if USE_NEW_CUSTOM_VALUE
11143 if (element_info[new_element].use_last_ce_value)
11144 CustomValue[x][y] = last_ce_value;
11147 InitField_WithBug1(x, y, FALSE);
11149 new_element = Feld[x][y]; /* element may have changed */
11151 #if USE_GFX_RESET_GFX_ANIMATION
11152 ResetGfxAnimation(x, y);
11153 ResetRandomAnimationValue(x, y);
11156 TEST_DrawLevelField(x, y);
11158 if (GFX_CRUMBLED(new_element))
11159 TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11163 /* check if element under the player changes from accessible to unaccessible
11164 (needed for special case of dropping element which then changes) */
11165 /* (must be checked after creating new element for walkable group elements) */
11166 #if USE_FIX_KILLED_BY_NON_WALKABLE
11167 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11168 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11175 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11176 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11185 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11186 if (new_element_is_player)
11187 RelocatePlayer(x, y, new_element);
11190 ChangeCount[x][y]++; /* count number of changes in the same frame */
11192 TestIfBadThingTouchesPlayer(x, y);
11193 TestIfPlayerTouchesCustomElement(x, y);
11194 TestIfElementTouchesCustomElement(x, y);
11197 static void CreateField(int x, int y, int element)
11199 CreateFieldExt(x, y, element, FALSE);
11202 static void CreateElementFromChange(int x, int y, int element)
11204 element = GET_VALID_RUNTIME_ELEMENT(element);
11206 #if USE_STOP_CHANGED_ELEMENTS
11207 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11209 int old_element = Feld[x][y];
11211 /* prevent changed element from moving in same engine frame
11212 unless both old and new element can either fall or move */
11213 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11214 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11219 CreateFieldExt(x, y, element, TRUE);
11222 static boolean ChangeElement(int x, int y, int element, int page)
11224 struct ElementInfo *ei = &element_info[element];
11225 struct ElementChangeInfo *change = &ei->change_page[page];
11226 int ce_value = CustomValue[x][y];
11227 int ce_score = ei->collect_score;
11228 int target_element;
11229 int old_element = Feld[x][y];
11231 /* always use default change event to prevent running into a loop */
11232 if (ChangeEvent[x][y] == -1)
11233 ChangeEvent[x][y] = CE_DELAY;
11235 if (ChangeEvent[x][y] == CE_DELAY)
11237 /* reset actual trigger element, trigger player and action element */
11238 change->actual_trigger_element = EL_EMPTY;
11239 change->actual_trigger_player = EL_EMPTY;
11240 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11241 change->actual_trigger_side = CH_SIDE_NONE;
11242 change->actual_trigger_ce_value = 0;
11243 change->actual_trigger_ce_score = 0;
11246 /* do not change elements more than a specified maximum number of changes */
11247 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11250 ChangeCount[x][y]++; /* count number of changes in the same frame */
11252 if (change->explode)
11259 if (change->use_target_content)
11261 boolean complete_replace = TRUE;
11262 boolean can_replace[3][3];
11265 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11268 boolean is_walkable;
11269 boolean is_diggable;
11270 boolean is_collectible;
11271 boolean is_removable;
11272 boolean is_destructible;
11273 int ex = x + xx - 1;
11274 int ey = y + yy - 1;
11275 int content_element = change->target_content.e[xx][yy];
11278 can_replace[xx][yy] = TRUE;
11280 if (ex == x && ey == y) /* do not check changing element itself */
11283 if (content_element == EL_EMPTY_SPACE)
11285 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11290 if (!IN_LEV_FIELD(ex, ey))
11292 can_replace[xx][yy] = FALSE;
11293 complete_replace = FALSE;
11300 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11301 e = MovingOrBlocked2Element(ex, ey);
11303 is_empty = (IS_FREE(ex, ey) ||
11304 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11306 is_walkable = (is_empty || IS_WALKABLE(e));
11307 is_diggable = (is_empty || IS_DIGGABLE(e));
11308 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11309 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11310 is_removable = (is_diggable || is_collectible);
11312 can_replace[xx][yy] =
11313 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11314 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11315 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11316 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11317 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11318 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11319 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11321 if (!can_replace[xx][yy])
11322 complete_replace = FALSE;
11325 if (!change->only_if_complete || complete_replace)
11327 boolean something_has_changed = FALSE;
11329 if (change->only_if_complete && change->use_random_replace &&
11330 RND(100) < change->random_percentage)
11333 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11335 int ex = x + xx - 1;
11336 int ey = y + yy - 1;
11337 int content_element;
11339 if (can_replace[xx][yy] && (!change->use_random_replace ||
11340 RND(100) < change->random_percentage))
11342 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11343 RemoveMovingField(ex, ey);
11345 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11347 content_element = change->target_content.e[xx][yy];
11348 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11349 ce_value, ce_score);
11351 CreateElementFromChange(ex, ey, target_element);
11353 something_has_changed = TRUE;
11355 /* for symmetry reasons, freeze newly created border elements */
11356 if (ex != x || ey != y)
11357 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11361 if (something_has_changed)
11363 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11364 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11370 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11371 ce_value, ce_score);
11373 if (element == EL_DIAGONAL_GROWING ||
11374 element == EL_DIAGONAL_SHRINKING)
11376 target_element = Store[x][y];
11378 Store[x][y] = EL_EMPTY;
11381 CreateElementFromChange(x, y, target_element);
11383 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11384 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11387 /* this uses direct change before indirect change */
11388 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11393 #if USE_NEW_DELAYED_ACTION
11395 static void HandleElementChange(int x, int y, int page)
11397 int element = MovingOrBlocked2Element(x, y);
11398 struct ElementInfo *ei = &element_info[element];
11399 struct ElementChangeInfo *change = &ei->change_page[page];
11400 boolean handle_action_before_change = FALSE;
11403 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11404 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11407 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11408 x, y, element, element_info[element].token_name);
11409 printf("HandleElementChange(): This should never happen!\n");
11414 /* this can happen with classic bombs on walkable, changing elements */
11415 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11418 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11419 ChangeDelay[x][y] = 0;
11425 if (ChangeDelay[x][y] == 0) /* initialize element change */
11427 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11429 if (change->can_change)
11432 /* !!! not clear why graphic animation should be reset at all here !!! */
11433 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11434 #if USE_GFX_RESET_WHEN_NOT_MOVING
11435 /* when a custom element is about to change (for example by change delay),
11436 do not reset graphic animation when the custom element is moving */
11437 if (!IS_MOVING(x, y))
11440 ResetGfxAnimation(x, y);
11441 ResetRandomAnimationValue(x, y);
11445 if (change->pre_change_function)
11446 change->pre_change_function(x, y);
11450 ChangeDelay[x][y]--;
11452 if (ChangeDelay[x][y] != 0) /* continue element change */
11454 if (change->can_change)
11456 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11458 if (IS_ANIMATED(graphic))
11459 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11461 if (change->change_function)
11462 change->change_function(x, y);
11465 else /* finish element change */
11467 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11469 page = ChangePage[x][y];
11470 ChangePage[x][y] = -1;
11472 change = &ei->change_page[page];
11475 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11477 ChangeDelay[x][y] = 1; /* try change after next move step */
11478 ChangePage[x][y] = page; /* remember page to use for change */
11484 /* special case: set new level random seed before changing element */
11485 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11486 handle_action_before_change = TRUE;
11488 if (change->has_action && handle_action_before_change)
11489 ExecuteCustomElementAction(x, y, element, page);
11492 if (change->can_change)
11494 if (ChangeElement(x, y, element, page))
11496 if (change->post_change_function)
11497 change->post_change_function(x, y);
11501 if (change->has_action && !handle_action_before_change)
11502 ExecuteCustomElementAction(x, y, element, page);
11508 static void HandleElementChange(int x, int y, int page)
11510 int element = MovingOrBlocked2Element(x, y);
11511 struct ElementInfo *ei = &element_info[element];
11512 struct ElementChangeInfo *change = &ei->change_page[page];
11515 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11518 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11519 x, y, element, element_info[element].token_name);
11520 printf("HandleElementChange(): This should never happen!\n");
11525 /* this can happen with classic bombs on walkable, changing elements */
11526 if (!CAN_CHANGE(element))
11529 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11530 ChangeDelay[x][y] = 0;
11536 if (ChangeDelay[x][y] == 0) /* initialize element change */
11538 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11540 ResetGfxAnimation(x, y);
11541 ResetRandomAnimationValue(x, y);
11543 if (change->pre_change_function)
11544 change->pre_change_function(x, y);
11547 ChangeDelay[x][y]--;
11549 if (ChangeDelay[x][y] != 0) /* continue element change */
11551 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11553 if (IS_ANIMATED(graphic))
11554 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11556 if (change->change_function)
11557 change->change_function(x, y);
11559 else /* finish element change */
11561 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11563 page = ChangePage[x][y];
11564 ChangePage[x][y] = -1;
11566 change = &ei->change_page[page];
11569 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11571 ChangeDelay[x][y] = 1; /* try change after next move step */
11572 ChangePage[x][y] = page; /* remember page to use for change */
11577 if (ChangeElement(x, y, element, page))
11579 if (change->post_change_function)
11580 change->post_change_function(x, y);
11587 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11588 int trigger_element,
11590 int trigger_player,
11594 boolean change_done_any = FALSE;
11595 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11598 if (!(trigger_events[trigger_element][trigger_event]))
11602 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11603 trigger_event, recursion_loop_depth, recursion_loop_detected,
11604 recursion_loop_element, EL_NAME(recursion_loop_element));
11607 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11609 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11611 int element = EL_CUSTOM_START + i;
11612 boolean change_done = FALSE;
11615 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11616 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11619 for (p = 0; p < element_info[element].num_change_pages; p++)
11621 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11623 if (change->can_change_or_has_action &&
11624 change->has_event[trigger_event] &&
11625 change->trigger_side & trigger_side &&
11626 change->trigger_player & trigger_player &&
11627 change->trigger_page & trigger_page_bits &&
11628 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11630 change->actual_trigger_element = trigger_element;
11631 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11632 change->actual_trigger_player_bits = trigger_player;
11633 change->actual_trigger_side = trigger_side;
11634 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11635 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11638 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11639 element, EL_NAME(element), p);
11642 if ((change->can_change && !change_done) || change->has_action)
11646 SCAN_PLAYFIELD(x, y)
11648 if (Feld[x][y] == element)
11650 if (change->can_change && !change_done)
11652 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11653 /* if element already changed in this frame, not only prevent
11654 another element change (checked in ChangeElement()), but
11655 also prevent additional element actions for this element */
11657 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11658 !level.use_action_after_change_bug)
11663 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11664 element, EL_NAME(element), p);
11667 ChangeDelay[x][y] = 1;
11668 ChangeEvent[x][y] = trigger_event;
11670 HandleElementChange(x, y, p);
11672 #if USE_NEW_DELAYED_ACTION
11673 else if (change->has_action)
11675 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11676 /* if element already changed in this frame, not only prevent
11677 another element change (checked in ChangeElement()), but
11678 also prevent additional element actions for this element */
11680 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11681 !level.use_action_after_change_bug)
11687 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11688 element, EL_NAME(element), p);
11691 ExecuteCustomElementAction(x, y, element, p);
11692 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11695 if (change->has_action)
11697 ExecuteCustomElementAction(x, y, element, p);
11698 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11704 if (change->can_change)
11706 change_done = TRUE;
11707 change_done_any = TRUE;
11710 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11711 element, EL_NAME(element), p);
11720 RECURSION_LOOP_DETECTION_END();
11722 return change_done_any;
11725 static boolean CheckElementChangeExt(int x, int y,
11727 int trigger_element,
11729 int trigger_player,
11732 boolean change_done = FALSE;
11735 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11736 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11739 if (Feld[x][y] == EL_BLOCKED)
11741 Blocked2Moving(x, y, &x, &y);
11742 element = Feld[x][y];
11746 /* check if element has already changed */
11747 if (Feld[x][y] != element)
11750 /* check if element has already changed or is about to change after moving */
11751 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11752 Feld[x][y] != element) ||
11754 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11755 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11756 ChangePage[x][y] != -1)))
11761 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11762 trigger_event, recursion_loop_depth, recursion_loop_detected,
11763 recursion_loop_element, EL_NAME(recursion_loop_element));
11766 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11769 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11772 for (p = 0; p < element_info[element].num_change_pages; p++)
11774 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11776 /* check trigger element for all events where the element that is checked
11777 for changing interacts with a directly adjacent element -- this is
11778 different to element changes that affect other elements to change on the
11779 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11780 boolean check_trigger_element =
11781 (trigger_event == CE_TOUCHING_X ||
11782 trigger_event == CE_HITTING_X ||
11783 trigger_event == CE_HIT_BY_X ||
11785 /* this one was forgotten until 3.2.3 */
11786 trigger_event == CE_DIGGING_X);
11789 if (change->can_change_or_has_action &&
11790 change->has_event[trigger_event] &&
11791 change->trigger_side & trigger_side &&
11792 change->trigger_player & trigger_player &&
11793 (!check_trigger_element ||
11794 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11796 change->actual_trigger_element = trigger_element;
11797 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11798 change->actual_trigger_player_bits = trigger_player;
11799 change->actual_trigger_side = trigger_side;
11800 change->actual_trigger_ce_value = CustomValue[x][y];
11801 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11803 /* special case: trigger element not at (x,y) position for some events */
11804 if (check_trigger_element)
11816 { 0, 0 }, { 0, 0 }, { 0, 0 },
11820 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11821 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11823 change->actual_trigger_ce_value = CustomValue[xx][yy];
11824 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11827 if (change->can_change && !change_done)
11829 ChangeDelay[x][y] = 1;
11830 ChangeEvent[x][y] = trigger_event;
11832 HandleElementChange(x, y, p);
11834 change_done = TRUE;
11836 #if USE_NEW_DELAYED_ACTION
11837 else if (change->has_action)
11839 ExecuteCustomElementAction(x, y, element, p);
11840 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11843 if (change->has_action)
11845 ExecuteCustomElementAction(x, y, element, p);
11846 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11852 RECURSION_LOOP_DETECTION_END();
11854 return change_done;
11857 static void PlayPlayerSound(struct PlayerInfo *player)
11859 int jx = player->jx, jy = player->jy;
11860 int sound_element = player->artwork_element;
11861 int last_action = player->last_action_waiting;
11862 int action = player->action_waiting;
11864 if (player->is_waiting)
11866 if (action != last_action)
11867 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11869 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11873 if (action != last_action)
11874 StopSound(element_info[sound_element].sound[last_action]);
11876 if (last_action == ACTION_SLEEPING)
11877 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11881 static void PlayAllPlayersSound()
11885 for (i = 0; i < MAX_PLAYERS; i++)
11886 if (stored_player[i].active)
11887 PlayPlayerSound(&stored_player[i]);
11890 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11892 boolean last_waiting = player->is_waiting;
11893 int move_dir = player->MovDir;
11895 player->dir_waiting = move_dir;
11896 player->last_action_waiting = player->action_waiting;
11900 if (!last_waiting) /* not waiting -> waiting */
11902 player->is_waiting = TRUE;
11904 player->frame_counter_bored =
11906 game.player_boring_delay_fixed +
11907 GetSimpleRandom(game.player_boring_delay_random);
11908 player->frame_counter_sleeping =
11910 game.player_sleeping_delay_fixed +
11911 GetSimpleRandom(game.player_sleeping_delay_random);
11913 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11916 if (game.player_sleeping_delay_fixed +
11917 game.player_sleeping_delay_random > 0 &&
11918 player->anim_delay_counter == 0 &&
11919 player->post_delay_counter == 0 &&
11920 FrameCounter >= player->frame_counter_sleeping)
11921 player->is_sleeping = TRUE;
11922 else if (game.player_boring_delay_fixed +
11923 game.player_boring_delay_random > 0 &&
11924 FrameCounter >= player->frame_counter_bored)
11925 player->is_bored = TRUE;
11927 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11928 player->is_bored ? ACTION_BORING :
11931 if (player->is_sleeping && player->use_murphy)
11933 /* special case for sleeping Murphy when leaning against non-free tile */
11935 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11936 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11937 !IS_MOVING(player->jx - 1, player->jy)))
11938 move_dir = MV_LEFT;
11939 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11940 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11941 !IS_MOVING(player->jx + 1, player->jy)))
11942 move_dir = MV_RIGHT;
11944 player->is_sleeping = FALSE;
11946 player->dir_waiting = move_dir;
11949 if (player->is_sleeping)
11951 if (player->num_special_action_sleeping > 0)
11953 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11955 int last_special_action = player->special_action_sleeping;
11956 int num_special_action = player->num_special_action_sleeping;
11957 int special_action =
11958 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11959 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11960 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11961 last_special_action + 1 : ACTION_SLEEPING);
11962 int special_graphic =
11963 el_act_dir2img(player->artwork_element, special_action, move_dir);
11965 player->anim_delay_counter =
11966 graphic_info[special_graphic].anim_delay_fixed +
11967 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11968 player->post_delay_counter =
11969 graphic_info[special_graphic].post_delay_fixed +
11970 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11972 player->special_action_sleeping = special_action;
11975 if (player->anim_delay_counter > 0)
11977 player->action_waiting = player->special_action_sleeping;
11978 player->anim_delay_counter--;
11980 else if (player->post_delay_counter > 0)
11982 player->post_delay_counter--;
11986 else if (player->is_bored)
11988 if (player->num_special_action_bored > 0)
11990 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11992 int special_action =
11993 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11994 int special_graphic =
11995 el_act_dir2img(player->artwork_element, special_action, move_dir);
11997 player->anim_delay_counter =
11998 graphic_info[special_graphic].anim_delay_fixed +
11999 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
12000 player->post_delay_counter =
12001 graphic_info[special_graphic].post_delay_fixed +
12002 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
12004 player->special_action_bored = special_action;
12007 if (player->anim_delay_counter > 0)
12009 player->action_waiting = player->special_action_bored;
12010 player->anim_delay_counter--;
12012 else if (player->post_delay_counter > 0)
12014 player->post_delay_counter--;
12019 else if (last_waiting) /* waiting -> not waiting */
12021 player->is_waiting = FALSE;
12022 player->is_bored = FALSE;
12023 player->is_sleeping = FALSE;
12025 player->frame_counter_bored = -1;
12026 player->frame_counter_sleeping = -1;
12028 player->anim_delay_counter = 0;
12029 player->post_delay_counter = 0;
12031 player->dir_waiting = player->MovDir;
12032 player->action_waiting = ACTION_DEFAULT;
12034 player->special_action_bored = ACTION_DEFAULT;
12035 player->special_action_sleeping = ACTION_DEFAULT;
12039 static void CheckSingleStepMode(struct PlayerInfo *player)
12041 if (tape.single_step && tape.recording && !tape.pausing)
12043 /* as it is called "single step mode", just return to pause mode when the
12044 player stopped moving after one tile (or never starts moving at all) */
12045 if (!player->is_moving && !player->is_pushing)
12047 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12048 SnapField(player, 0, 0); /* stop snapping */
12053 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12055 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12056 int left = player_action & JOY_LEFT;
12057 int right = player_action & JOY_RIGHT;
12058 int up = player_action & JOY_UP;
12059 int down = player_action & JOY_DOWN;
12060 int button1 = player_action & JOY_BUTTON_1;
12061 int button2 = player_action & JOY_BUTTON_2;
12062 int dx = (left ? -1 : right ? 1 : 0);
12063 int dy = (up ? -1 : down ? 1 : 0);
12065 if (!player->active || tape.pausing)
12071 snapped = SnapField(player, dx, dy);
12075 dropped = DropElement(player);
12077 moved = MovePlayer(player, dx, dy);
12080 CheckSingleStepMode(player);
12082 SetPlayerWaiting(player, FALSE);
12084 return player_action;
12088 /* no actions for this player (no input at player's configured device) */
12090 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12091 SnapField(player, 0, 0);
12092 CheckGravityMovementWhenNotMoving(player);
12094 if (player->MovPos == 0)
12095 SetPlayerWaiting(player, TRUE);
12097 if (player->MovPos == 0) /* needed for tape.playing */
12098 player->is_moving = FALSE;
12100 player->is_dropping = FALSE;
12101 player->is_dropping_pressed = FALSE;
12102 player->drop_pressed_delay = 0;
12104 CheckSingleStepMode(player);
12110 static void CheckLevelTime()
12114 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12115 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12117 if (level.native_em_level->lev->home == 0) /* all players at home */
12119 PlayerWins(local_player);
12121 AllPlayersGone = TRUE;
12123 level.native_em_level->lev->home = -1;
12126 if (level.native_em_level->ply[0]->alive == 0 &&
12127 level.native_em_level->ply[1]->alive == 0 &&
12128 level.native_em_level->ply[2]->alive == 0 &&
12129 level.native_em_level->ply[3]->alive == 0) /* all dead */
12130 AllPlayersGone = TRUE;
12132 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12134 if (game_sp.LevelSolved &&
12135 !game_sp.GameOver) /* game won */
12137 PlayerWins(local_player);
12139 game_sp.GameOver = TRUE;
12141 AllPlayersGone = TRUE;
12144 if (game_sp.GameOver) /* game lost */
12145 AllPlayersGone = TRUE;
12148 if (TimeFrames >= FRAMES_PER_SECOND)
12153 for (i = 0; i < MAX_PLAYERS; i++)
12155 struct PlayerInfo *player = &stored_player[i];
12157 if (SHIELD_ON(player))
12159 player->shield_normal_time_left--;
12161 if (player->shield_deadly_time_left > 0)
12162 player->shield_deadly_time_left--;
12166 if (!local_player->LevelSolved && !level.use_step_counter)
12174 if (TimeLeft <= 10 && setup.time_limit)
12175 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12178 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12180 DisplayGameControlValues();
12182 DrawGameValue_Time(TimeLeft);
12185 if (!TimeLeft && setup.time_limit)
12187 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12188 level.native_em_level->lev->killed_out_of_time = TRUE;
12190 for (i = 0; i < MAX_PLAYERS; i++)
12191 KillPlayer(&stored_player[i]);
12195 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12197 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12199 DisplayGameControlValues();
12202 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12203 DrawGameValue_Time(TimePlayed);
12206 level.native_em_level->lev->time =
12207 (level.time == 0 ? TimePlayed : TimeLeft);
12210 if (tape.recording || tape.playing)
12211 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12215 UpdateAndDisplayGameControlValues();
12217 UpdateGameDoorValues();
12218 DrawGameDoorValues();
12222 void AdvanceFrameAndPlayerCounters(int player_nr)
12226 /* advance frame counters (global frame counter and time frame counter) */
12230 /* advance player counters (counters for move delay, move animation etc.) */
12231 for (i = 0; i < MAX_PLAYERS; i++)
12233 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12234 int move_delay_value = stored_player[i].move_delay_value;
12235 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12237 if (!advance_player_counters) /* not all players may be affected */
12240 #if USE_NEW_PLAYER_ANIM
12241 if (move_frames == 0) /* less than one move per game frame */
12243 int stepsize = TILEX / move_delay_value;
12244 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12245 int count = (stored_player[i].is_moving ?
12246 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12248 if (count % delay == 0)
12253 stored_player[i].Frame += move_frames;
12255 if (stored_player[i].MovPos != 0)
12256 stored_player[i].StepFrame += move_frames;
12258 if (stored_player[i].move_delay > 0)
12259 stored_player[i].move_delay--;
12261 /* due to bugs in previous versions, counter must count up, not down */
12262 if (stored_player[i].push_delay != -1)
12263 stored_player[i].push_delay++;
12265 if (stored_player[i].drop_delay > 0)
12266 stored_player[i].drop_delay--;
12268 if (stored_player[i].is_dropping_pressed)
12269 stored_player[i].drop_pressed_delay++;
12273 void StartGameActions(boolean init_network_game, boolean record_tape,
12276 unsigned long new_random_seed = InitRND(random_seed);
12279 TapeStartRecording(new_random_seed);
12281 #if defined(NETWORK_AVALIABLE)
12282 if (init_network_game)
12284 SendToServer_StartPlaying();
12295 static unsigned long game_frame_delay = 0;
12296 unsigned long game_frame_delay_value;
12297 byte *recorded_player_action;
12298 byte summarized_player_action = 0;
12299 byte tape_action[MAX_PLAYERS];
12302 /* detect endless loops, caused by custom element programming */
12303 if (recursion_loop_detected && recursion_loop_depth == 0)
12305 char *message = getStringCat3("Internal Error ! Element ",
12306 EL_NAME(recursion_loop_element),
12307 " caused endless loop ! Quit the game ?");
12309 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12310 EL_NAME(recursion_loop_element));
12312 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12314 recursion_loop_detected = FALSE; /* if game should be continued */
12321 if (game.restart_level)
12322 StartGameActions(options.network, setup.autorecord, level.random_seed);
12324 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12325 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12327 if (level.native_em_level->lev->home == 0) /* all players at home */
12329 PlayerWins(local_player);
12331 AllPlayersGone = TRUE;
12333 level.native_em_level->lev->home = -1;
12336 if (level.native_em_level->ply[0]->alive == 0 &&
12337 level.native_em_level->ply[1]->alive == 0 &&
12338 level.native_em_level->ply[2]->alive == 0 &&
12339 level.native_em_level->ply[3]->alive == 0) /* all dead */
12340 AllPlayersGone = TRUE;
12342 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12344 if (game_sp.LevelSolved &&
12345 !game_sp.GameOver) /* game won */
12347 PlayerWins(local_player);
12349 game_sp.GameOver = TRUE;
12351 AllPlayersGone = TRUE;
12354 if (game_sp.GameOver) /* game lost */
12355 AllPlayersGone = TRUE;
12358 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12361 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12364 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12367 game_frame_delay_value =
12368 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12370 if (tape.playing && tape.warp_forward && !tape.pausing)
12371 game_frame_delay_value = 0;
12373 /* ---------- main game synchronization point ---------- */
12375 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12377 if (network_playing && !network_player_action_received)
12379 /* try to get network player actions in time */
12381 #if defined(NETWORK_AVALIABLE)
12382 /* last chance to get network player actions without main loop delay */
12383 HandleNetworking();
12386 /* game was quit by network peer */
12387 if (game_status != GAME_MODE_PLAYING)
12390 if (!network_player_action_received)
12391 return; /* failed to get network player actions in time */
12393 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12399 /* at this point we know that we really continue executing the game */
12401 network_player_action_received = FALSE;
12403 /* when playing tape, read previously recorded player input from tape data */
12404 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12407 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12412 if (tape.set_centered_player)
12414 game.centered_player_nr_next = tape.centered_player_nr_next;
12415 game.set_centered_player = TRUE;
12418 for (i = 0; i < MAX_PLAYERS; i++)
12420 summarized_player_action |= stored_player[i].action;
12422 if (!network_playing)
12423 stored_player[i].effective_action = stored_player[i].action;
12426 #if defined(NETWORK_AVALIABLE)
12427 if (network_playing)
12428 SendToServer_MovePlayer(summarized_player_action);
12431 if (!options.network && !setup.team_mode)
12432 local_player->effective_action = summarized_player_action;
12434 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12436 for (i = 0; i < MAX_PLAYERS; i++)
12437 stored_player[i].effective_action =
12438 (i == game.centered_player_nr ? summarized_player_action : 0);
12441 if (recorded_player_action != NULL)
12442 for (i = 0; i < MAX_PLAYERS; i++)
12443 stored_player[i].effective_action = recorded_player_action[i];
12445 for (i = 0; i < MAX_PLAYERS; i++)
12447 tape_action[i] = stored_player[i].effective_action;
12449 /* (this can only happen in the R'n'D game engine) */
12450 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12451 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12454 /* only record actions from input devices, but not programmed actions */
12455 if (tape.recording)
12456 TapeRecordAction(tape_action);
12458 #if USE_NEW_PLAYER_ASSIGNMENTS
12460 byte mapped_action[MAX_PLAYERS];
12462 for (i = 0; i < MAX_PLAYERS; i++)
12463 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12465 for (i = 0; i < MAX_PLAYERS; i++)
12466 stored_player[i].effective_action = mapped_action[i];
12470 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12472 GameActions_EM_Main();
12474 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12476 GameActions_SP_Main();
12484 void GameActions_EM_Main()
12486 byte effective_action[MAX_PLAYERS];
12487 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12490 for (i = 0; i < MAX_PLAYERS; i++)
12491 effective_action[i] = stored_player[i].effective_action;
12493 GameActions_EM(effective_action, warp_mode);
12497 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12500 void GameActions_SP_Main()
12502 byte effective_action[MAX_PLAYERS];
12503 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12506 for (i = 0; i < MAX_PLAYERS; i++)
12507 effective_action[i] = stored_player[i].effective_action;
12509 GameActions_SP(effective_action, warp_mode);
12513 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12516 void GameActions_RND()
12518 int magic_wall_x = 0, magic_wall_y = 0;
12519 int i, x, y, element, graphic;
12521 InitPlayfieldScanModeVars();
12523 #if USE_ONE_MORE_CHANGE_PER_FRAME
12524 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12526 SCAN_PLAYFIELD(x, y)
12528 ChangeCount[x][y] = 0;
12529 ChangeEvent[x][y] = -1;
12534 if (game.set_centered_player)
12536 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12538 /* switching to "all players" only possible if all players fit to screen */
12539 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12541 game.centered_player_nr_next = game.centered_player_nr;
12542 game.set_centered_player = FALSE;
12545 /* do not switch focus to non-existing (or non-active) player */
12546 if (game.centered_player_nr_next >= 0 &&
12547 !stored_player[game.centered_player_nr_next].active)
12549 game.centered_player_nr_next = game.centered_player_nr;
12550 game.set_centered_player = FALSE;
12554 if (game.set_centered_player &&
12555 ScreenMovPos == 0) /* screen currently aligned at tile position */
12559 if (game.centered_player_nr_next == -1)
12561 setScreenCenteredToAllPlayers(&sx, &sy);
12565 sx = stored_player[game.centered_player_nr_next].jx;
12566 sy = stored_player[game.centered_player_nr_next].jy;
12569 game.centered_player_nr = game.centered_player_nr_next;
12570 game.set_centered_player = FALSE;
12572 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12573 DrawGameDoorValues();
12576 for (i = 0; i < MAX_PLAYERS; i++)
12578 int actual_player_action = stored_player[i].effective_action;
12581 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12582 - rnd_equinox_tetrachloride 048
12583 - rnd_equinox_tetrachloride_ii 096
12584 - rnd_emanuel_schmieg 002
12585 - doctor_sloan_ww 001, 020
12587 if (stored_player[i].MovPos == 0)
12588 CheckGravityMovement(&stored_player[i]);
12591 /* overwrite programmed action with tape action */
12592 if (stored_player[i].programmed_action)
12593 actual_player_action = stored_player[i].programmed_action;
12595 PlayerActions(&stored_player[i], actual_player_action);
12597 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12600 ScrollScreen(NULL, SCROLL_GO_ON);
12602 /* for backwards compatibility, the following code emulates a fixed bug that
12603 occured when pushing elements (causing elements that just made their last
12604 pushing step to already (if possible) make their first falling step in the
12605 same game frame, which is bad); this code is also needed to use the famous
12606 "spring push bug" which is used in older levels and might be wanted to be
12607 used also in newer levels, but in this case the buggy pushing code is only
12608 affecting the "spring" element and no other elements */
12610 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12612 for (i = 0; i < MAX_PLAYERS; i++)
12614 struct PlayerInfo *player = &stored_player[i];
12615 int x = player->jx;
12616 int y = player->jy;
12618 if (player->active && player->is_pushing && player->is_moving &&
12620 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12621 Feld[x][y] == EL_SPRING))
12623 ContinueMoving(x, y);
12625 /* continue moving after pushing (this is actually a bug) */
12626 if (!IS_MOVING(x, y))
12627 Stop[x][y] = FALSE;
12633 debug_print_timestamp(0, "start main loop profiling");
12636 SCAN_PLAYFIELD(x, y)
12638 ChangeCount[x][y] = 0;
12639 ChangeEvent[x][y] = -1;
12641 /* this must be handled before main playfield loop */
12642 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12645 if (MovDelay[x][y] <= 0)
12649 #if USE_NEW_SNAP_DELAY
12650 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12653 if (MovDelay[x][y] <= 0)
12656 TEST_DrawLevelField(x, y);
12658 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12664 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12666 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12667 printf("GameActions(): This should never happen!\n");
12669 ChangePage[x][y] = -1;
12673 Stop[x][y] = FALSE;
12674 if (WasJustMoving[x][y] > 0)
12675 WasJustMoving[x][y]--;
12676 if (WasJustFalling[x][y] > 0)
12677 WasJustFalling[x][y]--;
12678 if (CheckCollision[x][y] > 0)
12679 CheckCollision[x][y]--;
12680 if (CheckImpact[x][y] > 0)
12681 CheckImpact[x][y]--;
12685 /* reset finished pushing action (not done in ContinueMoving() to allow
12686 continuous pushing animation for elements with zero push delay) */
12687 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12689 ResetGfxAnimation(x, y);
12690 TEST_DrawLevelField(x, y);
12694 if (IS_BLOCKED(x, y))
12698 Blocked2Moving(x, y, &oldx, &oldy);
12699 if (!IS_MOVING(oldx, oldy))
12701 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12702 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12703 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12704 printf("GameActions(): This should never happen!\n");
12711 debug_print_timestamp(0, "- time for pre-main loop:");
12714 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12715 SCAN_PLAYFIELD(x, y)
12717 element = Feld[x][y];
12718 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12723 int element2 = element;
12724 int graphic2 = graphic;
12726 int element2 = Feld[x][y];
12727 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12729 int last_gfx_frame = GfxFrame[x][y];
12731 if (graphic_info[graphic2].anim_global_sync)
12732 GfxFrame[x][y] = FrameCounter;
12733 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12734 GfxFrame[x][y] = CustomValue[x][y];
12735 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12736 GfxFrame[x][y] = element_info[element2].collect_score;
12737 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12738 GfxFrame[x][y] = ChangeDelay[x][y];
12740 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12741 DrawLevelGraphicAnimation(x, y, graphic2);
12744 ResetGfxFrame(x, y, TRUE);
12748 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12749 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12750 ResetRandomAnimationValue(x, y);
12754 SetRandomAnimationValue(x, y);
12758 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12761 #endif // -------------------- !!! TEST ONLY !!! --------------------
12764 debug_print_timestamp(0, "- time for TEST loop: -->");
12767 SCAN_PLAYFIELD(x, y)
12769 element = Feld[x][y];
12770 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12772 ResetGfxFrame(x, y, TRUE);
12774 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12775 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12776 ResetRandomAnimationValue(x, y);
12778 SetRandomAnimationValue(x, y);
12780 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12782 if (IS_INACTIVE(element))
12784 if (IS_ANIMATED(graphic))
12785 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12790 /* this may take place after moving, so 'element' may have changed */
12791 if (IS_CHANGING(x, y) &&
12792 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12794 int page = element_info[element].event_page_nr[CE_DELAY];
12797 HandleElementChange(x, y, page);
12799 if (CAN_CHANGE(element))
12800 HandleElementChange(x, y, page);
12802 if (HAS_ACTION(element))
12803 ExecuteCustomElementAction(x, y, element, page);
12806 element = Feld[x][y];
12807 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12810 #if 0 // ---------------------------------------------------------------------
12812 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12816 element = Feld[x][y];
12817 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12819 if (IS_ANIMATED(graphic) &&
12820 !IS_MOVING(x, y) &&
12822 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12824 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12825 TEST_DrawTwinkleOnField(x, y);
12827 else if (IS_MOVING(x, y))
12828 ContinueMoving(x, y);
12835 case EL_EM_EXIT_OPEN:
12836 case EL_SP_EXIT_OPEN:
12837 case EL_STEEL_EXIT_OPEN:
12838 case EL_EM_STEEL_EXIT_OPEN:
12839 case EL_SP_TERMINAL:
12840 case EL_SP_TERMINAL_ACTIVE:
12841 case EL_EXTRA_TIME:
12842 case EL_SHIELD_NORMAL:
12843 case EL_SHIELD_DEADLY:
12844 if (IS_ANIMATED(graphic))
12845 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12848 case EL_DYNAMITE_ACTIVE:
12849 case EL_EM_DYNAMITE_ACTIVE:
12850 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12851 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12852 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12853 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12854 case EL_SP_DISK_RED_ACTIVE:
12855 CheckDynamite(x, y);
12858 case EL_AMOEBA_GROWING:
12859 AmoebeWaechst(x, y);
12862 case EL_AMOEBA_SHRINKING:
12863 AmoebaDisappearing(x, y);
12866 #if !USE_NEW_AMOEBA_CODE
12867 case EL_AMOEBA_WET:
12868 case EL_AMOEBA_DRY:
12869 case EL_AMOEBA_FULL:
12871 case EL_EMC_DRIPPER:
12872 AmoebeAbleger(x, y);
12876 case EL_GAME_OF_LIFE:
12881 case EL_EXIT_CLOSED:
12885 case EL_EM_EXIT_CLOSED:
12889 case EL_STEEL_EXIT_CLOSED:
12890 CheckExitSteel(x, y);
12893 case EL_EM_STEEL_EXIT_CLOSED:
12894 CheckExitSteelEM(x, y);
12897 case EL_SP_EXIT_CLOSED:
12901 case EL_EXPANDABLE_WALL_GROWING:
12902 case EL_EXPANDABLE_STEELWALL_GROWING:
12903 MauerWaechst(x, y);
12906 case EL_EXPANDABLE_WALL:
12907 case EL_EXPANDABLE_WALL_HORIZONTAL:
12908 case EL_EXPANDABLE_WALL_VERTICAL:
12909 case EL_EXPANDABLE_WALL_ANY:
12910 case EL_BD_EXPANDABLE_WALL:
12911 MauerAbleger(x, y);
12914 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12915 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12916 case EL_EXPANDABLE_STEELWALL_ANY:
12917 MauerAblegerStahl(x, y);
12921 CheckForDragon(x, y);
12927 case EL_ELEMENT_SNAPPING:
12928 case EL_DIAGONAL_SHRINKING:
12929 case EL_DIAGONAL_GROWING:
12932 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12934 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12939 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12940 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12945 #else // ---------------------------------------------------------------------
12947 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12951 element = Feld[x][y];
12952 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12954 if (IS_ANIMATED(graphic) &&
12955 !IS_MOVING(x, y) &&
12957 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12959 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12960 TEST_DrawTwinkleOnField(x, y);
12962 else if ((element == EL_ACID ||
12963 element == EL_EXIT_OPEN ||
12964 element == EL_EM_EXIT_OPEN ||
12965 element == EL_SP_EXIT_OPEN ||
12966 element == EL_STEEL_EXIT_OPEN ||
12967 element == EL_EM_STEEL_EXIT_OPEN ||
12968 element == EL_SP_TERMINAL ||
12969 element == EL_SP_TERMINAL_ACTIVE ||
12970 element == EL_EXTRA_TIME ||
12971 element == EL_SHIELD_NORMAL ||
12972 element == EL_SHIELD_DEADLY) &&
12973 IS_ANIMATED(graphic))
12974 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12975 else if (IS_MOVING(x, y))
12976 ContinueMoving(x, y);
12977 else if (IS_ACTIVE_BOMB(element))
12978 CheckDynamite(x, y);
12979 else if (element == EL_AMOEBA_GROWING)
12980 AmoebeWaechst(x, y);
12981 else if (element == EL_AMOEBA_SHRINKING)
12982 AmoebaDisappearing(x, y);
12984 #if !USE_NEW_AMOEBA_CODE
12985 else if (IS_AMOEBALIVE(element))
12986 AmoebeAbleger(x, y);
12989 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12991 else if (element == EL_EXIT_CLOSED)
12993 else if (element == EL_EM_EXIT_CLOSED)
12995 else if (element == EL_STEEL_EXIT_CLOSED)
12996 CheckExitSteel(x, y);
12997 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12998 CheckExitSteelEM(x, y);
12999 else if (element == EL_SP_EXIT_CLOSED)
13001 else if (element == EL_EXPANDABLE_WALL_GROWING ||
13002 element == EL_EXPANDABLE_STEELWALL_GROWING)
13003 MauerWaechst(x, y);
13004 else if (element == EL_EXPANDABLE_WALL ||
13005 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
13006 element == EL_EXPANDABLE_WALL_VERTICAL ||
13007 element == EL_EXPANDABLE_WALL_ANY ||
13008 element == EL_BD_EXPANDABLE_WALL)
13009 MauerAbleger(x, y);
13010 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
13011 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
13012 element == EL_EXPANDABLE_STEELWALL_ANY)
13013 MauerAblegerStahl(x, y);
13014 else if (element == EL_FLAMES)
13015 CheckForDragon(x, y);
13016 else if (element == EL_EXPLOSION)
13017 ; /* drawing of correct explosion animation is handled separately */
13018 else if (element == EL_ELEMENT_SNAPPING ||
13019 element == EL_DIAGONAL_SHRINKING ||
13020 element == EL_DIAGONAL_GROWING)
13022 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
13024 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13026 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
13027 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
13029 #endif // ---------------------------------------------------------------------
13031 if (IS_BELT_ACTIVE(element))
13032 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
13034 if (game.magic_wall_active)
13036 int jx = local_player->jx, jy = local_player->jy;
13038 /* play the element sound at the position nearest to the player */
13039 if ((element == EL_MAGIC_WALL_FULL ||
13040 element == EL_MAGIC_WALL_ACTIVE ||
13041 element == EL_MAGIC_WALL_EMPTYING ||
13042 element == EL_BD_MAGIC_WALL_FULL ||
13043 element == EL_BD_MAGIC_WALL_ACTIVE ||
13044 element == EL_BD_MAGIC_WALL_EMPTYING ||
13045 element == EL_DC_MAGIC_WALL_FULL ||
13046 element == EL_DC_MAGIC_WALL_ACTIVE ||
13047 element == EL_DC_MAGIC_WALL_EMPTYING) &&
13048 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13057 debug_print_timestamp(0, "- time for MAIN loop: -->");
13060 #if USE_NEW_AMOEBA_CODE
13061 /* new experimental amoeba growth stuff */
13062 if (!(FrameCounter % 8))
13064 static unsigned long random = 1684108901;
13066 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13068 x = RND(lev_fieldx);
13069 y = RND(lev_fieldy);
13070 element = Feld[x][y];
13072 if (!IS_PLAYER(x,y) &&
13073 (element == EL_EMPTY ||
13074 CAN_GROW_INTO(element) ||
13075 element == EL_QUICKSAND_EMPTY ||
13076 element == EL_QUICKSAND_FAST_EMPTY ||
13077 element == EL_ACID_SPLASH_LEFT ||
13078 element == EL_ACID_SPLASH_RIGHT))
13080 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13081 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13082 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13083 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13084 Feld[x][y] = EL_AMOEBA_DROP;
13087 random = random * 129 + 1;
13093 if (game.explosions_delayed)
13096 game.explosions_delayed = FALSE;
13098 SCAN_PLAYFIELD(x, y)
13100 element = Feld[x][y];
13102 if (ExplodeField[x][y])
13103 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13104 else if (element == EL_EXPLOSION)
13105 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13107 ExplodeField[x][y] = EX_TYPE_NONE;
13110 game.explosions_delayed = TRUE;
13113 if (game.magic_wall_active)
13115 if (!(game.magic_wall_time_left % 4))
13117 int element = Feld[magic_wall_x][magic_wall_y];
13119 if (element == EL_BD_MAGIC_WALL_FULL ||
13120 element == EL_BD_MAGIC_WALL_ACTIVE ||
13121 element == EL_BD_MAGIC_WALL_EMPTYING)
13122 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13123 else if (element == EL_DC_MAGIC_WALL_FULL ||
13124 element == EL_DC_MAGIC_WALL_ACTIVE ||
13125 element == EL_DC_MAGIC_WALL_EMPTYING)
13126 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13128 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13131 if (game.magic_wall_time_left > 0)
13133 game.magic_wall_time_left--;
13135 if (!game.magic_wall_time_left)
13137 SCAN_PLAYFIELD(x, y)
13139 element = Feld[x][y];
13141 if (element == EL_MAGIC_WALL_ACTIVE ||
13142 element == EL_MAGIC_WALL_FULL)
13144 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13145 TEST_DrawLevelField(x, y);
13147 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13148 element == EL_BD_MAGIC_WALL_FULL)
13150 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13151 TEST_DrawLevelField(x, y);
13153 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13154 element == EL_DC_MAGIC_WALL_FULL)
13156 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13157 TEST_DrawLevelField(x, y);
13161 game.magic_wall_active = FALSE;
13166 if (game.light_time_left > 0)
13168 game.light_time_left--;
13170 if (game.light_time_left == 0)
13171 RedrawAllLightSwitchesAndInvisibleElements();
13174 if (game.timegate_time_left > 0)
13176 game.timegate_time_left--;
13178 if (game.timegate_time_left == 0)
13179 CloseAllOpenTimegates();
13182 if (game.lenses_time_left > 0)
13184 game.lenses_time_left--;
13186 if (game.lenses_time_left == 0)
13187 RedrawAllInvisibleElementsForLenses();
13190 if (game.magnify_time_left > 0)
13192 game.magnify_time_left--;
13194 if (game.magnify_time_left == 0)
13195 RedrawAllInvisibleElementsForMagnifier();
13198 for (i = 0; i < MAX_PLAYERS; i++)
13200 struct PlayerInfo *player = &stored_player[i];
13202 if (SHIELD_ON(player))
13204 if (player->shield_deadly_time_left)
13205 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13206 else if (player->shield_normal_time_left)
13207 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13211 #if USE_DELAYED_GFX_REDRAW
13212 SCAN_PLAYFIELD(x, y)
13215 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13217 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13218 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13221 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13222 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13224 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13225 DrawLevelField(x, y);
13227 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13228 DrawLevelFieldCrumbled(x, y);
13230 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13231 DrawLevelFieldCrumbledNeighbours(x, y);
13233 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13234 DrawTwinkleOnField(x, y);
13237 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13244 PlayAllPlayersSound();
13246 if (options.debug) /* calculate frames per second */
13248 static unsigned long fps_counter = 0;
13249 static int fps_frames = 0;
13250 unsigned long fps_delay_ms = Counter() - fps_counter;
13254 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13256 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13259 fps_counter = Counter();
13262 redraw_mask |= REDRAW_FPS;
13265 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13267 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13269 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13271 local_player->show_envelope = 0;
13275 debug_print_timestamp(0, "stop main loop profiling ");
13276 printf("----------------------------------------------------------\n");
13279 /* use random number generator in every frame to make it less predictable */
13280 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13284 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13286 int min_x = x, min_y = y, max_x = x, max_y = y;
13289 for (i = 0; i < MAX_PLAYERS; i++)
13291 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13293 if (!stored_player[i].active || &stored_player[i] == player)
13296 min_x = MIN(min_x, jx);
13297 min_y = MIN(min_y, jy);
13298 max_x = MAX(max_x, jx);
13299 max_y = MAX(max_y, jy);
13302 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13305 static boolean AllPlayersInVisibleScreen()
13309 for (i = 0; i < MAX_PLAYERS; i++)
13311 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13313 if (!stored_player[i].active)
13316 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13323 void ScrollLevel(int dx, int dy)
13326 /* (directly solved in BlitBitmap() now) */
13327 static Bitmap *bitmap_db_field2 = NULL;
13328 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13335 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13336 /* only horizontal XOR vertical scroll direction allowed */
13337 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13342 /* (directly solved in BlitBitmap() now) */
13343 if (bitmap_db_field2 == NULL)
13344 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13346 /* needed when blitting directly to same bitmap -- should not be needed with
13347 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13348 BlitBitmap(drawto_field, bitmap_db_field2,
13349 FX + TILEX * (dx == -1) - softscroll_offset,
13350 FY + TILEY * (dy == -1) - softscroll_offset,
13351 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13352 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13353 FX + TILEX * (dx == 1) - softscroll_offset,
13354 FY + TILEY * (dy == 1) - softscroll_offset);
13355 BlitBitmap(bitmap_db_field2, drawto_field,
13356 FX + TILEX * (dx == 1) - softscroll_offset,
13357 FY + TILEY * (dy == 1) - softscroll_offset,
13358 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13359 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13360 FX + TILEX * (dx == 1) - softscroll_offset,
13361 FY + TILEY * (dy == 1) - softscroll_offset);
13366 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13367 int xsize = (BX2 - BX1 + 1);
13368 int ysize = (BY2 - BY1 + 1);
13369 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13370 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13371 int step = (start < end ? +1 : -1);
13373 for (i = start; i != end; i += step)
13375 BlitBitmap(drawto_field, drawto_field,
13376 FX + TILEX * (dx != 0 ? i + step : 0),
13377 FY + TILEY * (dy != 0 ? i + step : 0),
13378 TILEX * (dx != 0 ? 1 : xsize),
13379 TILEY * (dy != 0 ? 1 : ysize),
13380 FX + TILEX * (dx != 0 ? i : 0),
13381 FY + TILEY * (dy != 0 ? i : 0));
13388 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13390 int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13394 int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13396 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13401 BlitBitmap(drawto_field, drawto_field,
13402 FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13403 FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13404 SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13405 SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13406 FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13407 FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13409 BlitBitmap(drawto_field, drawto_field,
13410 FX + TILEX * (dx == -1) - softscroll_offset,
13411 FY + TILEY * (dy == -1) - softscroll_offset,
13412 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13413 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13414 FX + TILEX * (dx == 1) - softscroll_offset,
13415 FY + TILEY * (dy == 1) - softscroll_offset);
13423 x = (dx == 1 ? BX1 : BX2);
13424 for (y = BY1; y <= BY2; y++)
13425 DrawScreenField(x, y);
13430 y = (dy == 1 ? BY1 : BY2);
13431 for (x = BX1; x <= BX2; x++)
13432 DrawScreenField(x, y);
13435 redraw_mask |= REDRAW_FIELD;
13438 static boolean canFallDown(struct PlayerInfo *player)
13440 int jx = player->jx, jy = player->jy;
13442 return (IN_LEV_FIELD(jx, jy + 1) &&
13443 (IS_FREE(jx, jy + 1) ||
13444 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13445 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13446 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13449 static boolean canPassField(int x, int y, int move_dir)
13451 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13452 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13453 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13454 int nextx = x + dx;
13455 int nexty = y + dy;
13456 int element = Feld[x][y];
13458 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13459 !CAN_MOVE(element) &&
13460 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13461 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13462 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13465 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13467 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13468 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13469 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13473 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13474 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13475 (IS_DIGGABLE(Feld[newx][newy]) ||
13476 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13477 canPassField(newx, newy, move_dir)));
13480 static void CheckGravityMovement(struct PlayerInfo *player)
13482 #if USE_PLAYER_GRAVITY
13483 if (player->gravity && !player->programmed_action)
13485 if (game.gravity && !player->programmed_action)
13488 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13489 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13490 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13491 int jx = player->jx, jy = player->jy;
13492 boolean player_is_moving_to_valid_field =
13493 (!player_is_snapping &&
13494 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13495 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13496 boolean player_can_fall_down = canFallDown(player);
13498 if (player_can_fall_down &&
13499 !player_is_moving_to_valid_field)
13500 player->programmed_action = MV_DOWN;
13504 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13506 return CheckGravityMovement(player);
13508 #if USE_PLAYER_GRAVITY
13509 if (player->gravity && !player->programmed_action)
13511 if (game.gravity && !player->programmed_action)
13514 int jx = player->jx, jy = player->jy;
13515 boolean field_under_player_is_free =
13516 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13517 boolean player_is_standing_on_valid_field =
13518 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13519 (IS_WALKABLE(Feld[jx][jy]) &&
13520 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13522 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13523 player->programmed_action = MV_DOWN;
13528 MovePlayerOneStep()
13529 -----------------------------------------------------------------------------
13530 dx, dy: direction (non-diagonal) to try to move the player to
13531 real_dx, real_dy: direction as read from input device (can be diagonal)
13534 boolean MovePlayerOneStep(struct PlayerInfo *player,
13535 int dx, int dy, int real_dx, int real_dy)
13537 int jx = player->jx, jy = player->jy;
13538 int new_jx = jx + dx, new_jy = jy + dy;
13539 #if !USE_FIXED_DONT_RUN_INTO
13543 boolean player_can_move = !player->cannot_move;
13545 if (!player->active || (!dx && !dy))
13546 return MP_NO_ACTION;
13548 player->MovDir = (dx < 0 ? MV_LEFT :
13549 dx > 0 ? MV_RIGHT :
13551 dy > 0 ? MV_DOWN : MV_NONE);
13553 if (!IN_LEV_FIELD(new_jx, new_jy))
13554 return MP_NO_ACTION;
13556 if (!player_can_move)
13558 if (player->MovPos == 0)
13560 player->is_moving = FALSE;
13561 player->is_digging = FALSE;
13562 player->is_collecting = FALSE;
13563 player->is_snapping = FALSE;
13564 player->is_pushing = FALSE;
13569 if (!options.network && game.centered_player_nr == -1 &&
13570 !AllPlayersInSight(player, new_jx, new_jy))
13571 return MP_NO_ACTION;
13573 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13574 return MP_NO_ACTION;
13577 #if !USE_FIXED_DONT_RUN_INTO
13578 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13580 /* (moved to DigField()) */
13581 if (player_can_move && DONT_RUN_INTO(element))
13583 if (element == EL_ACID && dx == 0 && dy == 1)
13585 SplashAcid(new_jx, new_jy);
13586 Feld[jx][jy] = EL_PLAYER_1;
13587 InitMovingField(jx, jy, MV_DOWN);
13588 Store[jx][jy] = EL_ACID;
13589 ContinueMoving(jx, jy);
13590 BuryPlayer(player);
13593 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13599 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13600 if (can_move != MP_MOVING)
13603 /* check if DigField() has caused relocation of the player */
13604 if (player->jx != jx || player->jy != jy)
13605 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13607 StorePlayer[jx][jy] = 0;
13608 player->last_jx = jx;
13609 player->last_jy = jy;
13610 player->jx = new_jx;
13611 player->jy = new_jy;
13612 StorePlayer[new_jx][new_jy] = player->element_nr;
13614 if (player->move_delay_value_next != -1)
13616 player->move_delay_value = player->move_delay_value_next;
13617 player->move_delay_value_next = -1;
13621 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13623 player->step_counter++;
13625 PlayerVisit[jx][jy] = FrameCounter;
13627 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13628 player->is_moving = TRUE;
13632 /* should better be called in MovePlayer(), but this breaks some tapes */
13633 ScrollPlayer(player, SCROLL_INIT);
13639 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13641 int jx = player->jx, jy = player->jy;
13642 int old_jx = jx, old_jy = jy;
13643 int moved = MP_NO_ACTION;
13645 if (!player->active)
13650 if (player->MovPos == 0)
13652 player->is_moving = FALSE;
13653 player->is_digging = FALSE;
13654 player->is_collecting = FALSE;
13655 player->is_snapping = FALSE;
13656 player->is_pushing = FALSE;
13662 if (player->move_delay > 0)
13665 player->move_delay = -1; /* set to "uninitialized" value */
13667 /* store if player is automatically moved to next field */
13668 player->is_auto_moving = (player->programmed_action != MV_NONE);
13670 /* remove the last programmed player action */
13671 player->programmed_action = 0;
13673 if (player->MovPos)
13675 /* should only happen if pre-1.2 tape recordings are played */
13676 /* this is only for backward compatibility */
13678 int original_move_delay_value = player->move_delay_value;
13681 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13685 /* scroll remaining steps with finest movement resolution */
13686 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13688 while (player->MovPos)
13690 ScrollPlayer(player, SCROLL_GO_ON);
13691 ScrollScreen(NULL, SCROLL_GO_ON);
13693 AdvanceFrameAndPlayerCounters(player->index_nr);
13699 player->move_delay_value = original_move_delay_value;
13702 player->is_active = FALSE;
13704 if (player->last_move_dir & MV_HORIZONTAL)
13706 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13707 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13711 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13712 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13715 #if USE_FIXED_BORDER_RUNNING_GFX
13716 if (!moved && !player->is_active)
13718 player->is_moving = FALSE;
13719 player->is_digging = FALSE;
13720 player->is_collecting = FALSE;
13721 player->is_snapping = FALSE;
13722 player->is_pushing = FALSE;
13730 if (moved & MP_MOVING && !ScreenMovPos &&
13731 (player->index_nr == game.centered_player_nr ||
13732 game.centered_player_nr == -1))
13734 if (moved & MP_MOVING && !ScreenMovPos &&
13735 (player == local_player || !options.network))
13738 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13739 int offset = game.scroll_delay_value;
13741 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13743 /* actual player has left the screen -- scroll in that direction */
13744 if (jx != old_jx) /* player has moved horizontally */
13745 scroll_x += (jx - old_jx);
13746 else /* player has moved vertically */
13747 scroll_y += (jy - old_jy);
13751 if (jx != old_jx) /* player has moved horizontally */
13753 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13754 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13755 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13757 /* don't scroll over playfield boundaries */
13758 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13759 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13761 /* don't scroll more than one field at a time */
13762 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13764 /* don't scroll against the player's moving direction */
13765 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13766 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13767 scroll_x = old_scroll_x;
13769 else /* player has moved vertically */
13771 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13772 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13773 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13775 /* don't scroll over playfield boundaries */
13776 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13777 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13779 /* don't scroll more than one field at a time */
13780 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13782 /* don't scroll against the player's moving direction */
13783 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13784 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13785 scroll_y = old_scroll_y;
13789 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13792 if (!options.network && game.centered_player_nr == -1 &&
13793 !AllPlayersInVisibleScreen())
13795 scroll_x = old_scroll_x;
13796 scroll_y = old_scroll_y;
13800 if (!options.network && !AllPlayersInVisibleScreen())
13802 scroll_x = old_scroll_x;
13803 scroll_y = old_scroll_y;
13808 ScrollScreen(player, SCROLL_INIT);
13809 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13814 player->StepFrame = 0;
13816 if (moved & MP_MOVING)
13818 if (old_jx != jx && old_jy == jy)
13819 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13820 else if (old_jx == jx && old_jy != jy)
13821 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13823 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13825 player->last_move_dir = player->MovDir;
13826 player->is_moving = TRUE;
13827 player->is_snapping = FALSE;
13828 player->is_switching = FALSE;
13829 player->is_dropping = FALSE;
13830 player->is_dropping_pressed = FALSE;
13831 player->drop_pressed_delay = 0;
13834 /* should better be called here than above, but this breaks some tapes */
13835 ScrollPlayer(player, SCROLL_INIT);
13840 CheckGravityMovementWhenNotMoving(player);
13842 player->is_moving = FALSE;
13844 /* at this point, the player is allowed to move, but cannot move right now
13845 (e.g. because of something blocking the way) -- ensure that the player
13846 is also allowed to move in the next frame (in old versions before 3.1.1,
13847 the player was forced to wait again for eight frames before next try) */
13849 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13850 player->move_delay = 0; /* allow direct movement in the next frame */
13853 if (player->move_delay == -1) /* not yet initialized by DigField() */
13854 player->move_delay = player->move_delay_value;
13856 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13858 TestIfPlayerTouchesBadThing(jx, jy);
13859 TestIfPlayerTouchesCustomElement(jx, jy);
13862 if (!player->active)
13863 RemovePlayer(player);
13868 void ScrollPlayer(struct PlayerInfo *player, int mode)
13870 int jx = player->jx, jy = player->jy;
13871 int last_jx = player->last_jx, last_jy = player->last_jy;
13872 int move_stepsize = TILEX / player->move_delay_value;
13874 #if USE_NEW_PLAYER_SPEED
13875 if (!player->active)
13878 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13881 if (!player->active || player->MovPos == 0)
13885 if (mode == SCROLL_INIT)
13887 player->actual_frame_counter = FrameCounter;
13888 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13890 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13891 Feld[last_jx][last_jy] == EL_EMPTY)
13893 int last_field_block_delay = 0; /* start with no blocking at all */
13894 int block_delay_adjustment = player->block_delay_adjustment;
13896 /* if player blocks last field, add delay for exactly one move */
13897 if (player->block_last_field)
13899 last_field_block_delay += player->move_delay_value;
13901 /* when blocking enabled, prevent moving up despite gravity */
13902 #if USE_PLAYER_GRAVITY
13903 if (player->gravity && player->MovDir == MV_UP)
13904 block_delay_adjustment = -1;
13906 if (game.gravity && player->MovDir == MV_UP)
13907 block_delay_adjustment = -1;
13911 /* add block delay adjustment (also possible when not blocking) */
13912 last_field_block_delay += block_delay_adjustment;
13914 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13915 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13918 #if USE_NEW_PLAYER_SPEED
13919 if (player->MovPos != 0) /* player has not yet reached destination */
13925 else if (!FrameReached(&player->actual_frame_counter, 1))
13928 #if USE_NEW_PLAYER_SPEED
13929 if (player->MovPos != 0)
13931 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13932 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13934 /* before DrawPlayer() to draw correct player graphic for this case */
13935 if (player->MovPos == 0)
13936 CheckGravityMovement(player);
13939 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13940 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13942 /* before DrawPlayer() to draw correct player graphic for this case */
13943 if (player->MovPos == 0)
13944 CheckGravityMovement(player);
13947 if (player->MovPos == 0) /* player reached destination field */
13949 if (player->move_delay_reset_counter > 0)
13951 player->move_delay_reset_counter--;
13953 if (player->move_delay_reset_counter == 0)
13955 /* continue with normal speed after quickly moving through gate */
13956 HALVE_PLAYER_SPEED(player);
13958 /* be able to make the next move without delay */
13959 player->move_delay = 0;
13963 player->last_jx = jx;
13964 player->last_jy = jy;
13966 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13967 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13969 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13971 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13972 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13974 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13976 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13977 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13979 DrawPlayer(player); /* needed here only to cleanup last field */
13980 RemovePlayer(player);
13982 if (local_player->friends_still_needed == 0 ||
13983 IS_SP_ELEMENT(Feld[jx][jy]))
13984 PlayerWins(player);
13987 /* this breaks one level: "machine", level 000 */
13989 int move_direction = player->MovDir;
13990 int enter_side = MV_DIR_OPPOSITE(move_direction);
13991 int leave_side = move_direction;
13992 int old_jx = last_jx;
13993 int old_jy = last_jy;
13994 int old_element = Feld[old_jx][old_jy];
13995 int new_element = Feld[jx][jy];
13997 if (IS_CUSTOM_ELEMENT(old_element))
13998 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
14000 player->index_bit, leave_side);
14002 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
14003 CE_PLAYER_LEAVES_X,
14004 player->index_bit, leave_side);
14006 if (IS_CUSTOM_ELEMENT(new_element))
14007 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
14008 player->index_bit, enter_side);
14010 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
14011 CE_PLAYER_ENTERS_X,
14012 player->index_bit, enter_side);
14014 #if USE_FIX_CE_ACTION_WITH_PLAYER
14015 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
14016 CE_MOVE_OF_X, move_direction);
14018 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
14019 CE_MOVE_OF_X, move_direction);
14023 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14025 TestIfPlayerTouchesBadThing(jx, jy);
14026 TestIfPlayerTouchesCustomElement(jx, jy);
14028 /* needed because pushed element has not yet reached its destination,
14029 so it would trigger a change event at its previous field location */
14030 if (!player->is_pushing)
14031 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
14033 if (!player->active)
14034 RemovePlayer(player);
14037 if (!local_player->LevelSolved && level.use_step_counter)
14047 if (TimeLeft <= 10 && setup.time_limit)
14048 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14051 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14053 DisplayGameControlValues();
14055 DrawGameValue_Time(TimeLeft);
14058 if (!TimeLeft && setup.time_limit)
14059 for (i = 0; i < MAX_PLAYERS; i++)
14060 KillPlayer(&stored_player[i]);
14063 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14065 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14067 DisplayGameControlValues();
14070 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14071 DrawGameValue_Time(TimePlayed);
14075 if (tape.single_step && tape.recording && !tape.pausing &&
14076 !player->programmed_action)
14077 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14081 void ScrollScreen(struct PlayerInfo *player, int mode)
14083 static unsigned long screen_frame_counter = 0;
14085 if (mode == SCROLL_INIT)
14087 /* set scrolling step size according to actual player's moving speed */
14088 ScrollStepSize = TILEX / player->move_delay_value;
14090 screen_frame_counter = FrameCounter;
14091 ScreenMovDir = player->MovDir;
14092 ScreenMovPos = player->MovPos;
14093 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14096 else if (!FrameReached(&screen_frame_counter, 1))
14101 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14102 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14103 redraw_mask |= REDRAW_FIELD;
14106 ScreenMovDir = MV_NONE;
14109 void TestIfPlayerTouchesCustomElement(int x, int y)
14111 static int xy[4][2] =
14118 static int trigger_sides[4][2] =
14120 /* center side border side */
14121 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14122 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14123 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14124 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14126 static int touch_dir[4] =
14128 MV_LEFT | MV_RIGHT,
14133 int center_element = Feld[x][y]; /* should always be non-moving! */
14136 for (i = 0; i < NUM_DIRECTIONS; i++)
14138 int xx = x + xy[i][0];
14139 int yy = y + xy[i][1];
14140 int center_side = trigger_sides[i][0];
14141 int border_side = trigger_sides[i][1];
14142 int border_element;
14144 if (!IN_LEV_FIELD(xx, yy))
14147 if (IS_PLAYER(x, y)) /* player found at center element */
14149 struct PlayerInfo *player = PLAYERINFO(x, y);
14151 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14152 border_element = Feld[xx][yy]; /* may be moving! */
14153 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14154 border_element = Feld[xx][yy];
14155 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14156 border_element = MovingOrBlocked2Element(xx, yy);
14158 continue; /* center and border element do not touch */
14160 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14161 player->index_bit, border_side);
14162 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14163 CE_PLAYER_TOUCHES_X,
14164 player->index_bit, border_side);
14166 #if USE_FIX_CE_ACTION_WITH_PLAYER
14168 /* use player element that is initially defined in the level playfield,
14169 not the player element that corresponds to the runtime player number
14170 (example: a level that contains EL_PLAYER_3 as the only player would
14171 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14172 int player_element = PLAYERINFO(x, y)->initial_element;
14174 CheckElementChangeBySide(xx, yy, border_element, player_element,
14175 CE_TOUCHING_X, border_side);
14179 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14181 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14183 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14185 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14186 continue; /* center and border element do not touch */
14189 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14190 player->index_bit, center_side);
14191 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14192 CE_PLAYER_TOUCHES_X,
14193 player->index_bit, center_side);
14195 #if USE_FIX_CE_ACTION_WITH_PLAYER
14197 /* use player element that is initially defined in the level playfield,
14198 not the player element that corresponds to the runtime player number
14199 (example: a level that contains EL_PLAYER_3 as the only player would
14200 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14201 int player_element = PLAYERINFO(xx, yy)->initial_element;
14203 CheckElementChangeBySide(x, y, center_element, player_element,
14204 CE_TOUCHING_X, center_side);
14213 #if USE_ELEMENT_TOUCHING_BUGFIX
14215 void TestIfElementTouchesCustomElement(int x, int y)
14217 static int xy[4][2] =
14224 static int trigger_sides[4][2] =
14226 /* center side border side */
14227 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14228 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14229 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14230 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14232 static int touch_dir[4] =
14234 MV_LEFT | MV_RIGHT,
14239 boolean change_center_element = FALSE;
14240 int center_element = Feld[x][y]; /* should always be non-moving! */
14241 int border_element_old[NUM_DIRECTIONS];
14244 for (i = 0; i < NUM_DIRECTIONS; i++)
14246 int xx = x + xy[i][0];
14247 int yy = y + xy[i][1];
14248 int border_element;
14250 border_element_old[i] = -1;
14252 if (!IN_LEV_FIELD(xx, yy))
14255 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14256 border_element = Feld[xx][yy]; /* may be moving! */
14257 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14258 border_element = Feld[xx][yy];
14259 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14260 border_element = MovingOrBlocked2Element(xx, yy);
14262 continue; /* center and border element do not touch */
14264 border_element_old[i] = border_element;
14267 for (i = 0; i < NUM_DIRECTIONS; i++)
14269 int xx = x + xy[i][0];
14270 int yy = y + xy[i][1];
14271 int center_side = trigger_sides[i][0];
14272 int border_element = border_element_old[i];
14274 if (border_element == -1)
14277 /* check for change of border element */
14278 CheckElementChangeBySide(xx, yy, border_element, center_element,
14279 CE_TOUCHING_X, center_side);
14281 /* (center element cannot be player, so we dont have to check this here) */
14284 for (i = 0; i < NUM_DIRECTIONS; i++)
14286 int xx = x + xy[i][0];
14287 int yy = y + xy[i][1];
14288 int border_side = trigger_sides[i][1];
14289 int border_element = border_element_old[i];
14291 if (border_element == -1)
14294 /* check for change of center element (but change it only once) */
14295 if (!change_center_element)
14296 change_center_element =
14297 CheckElementChangeBySide(x, y, center_element, border_element,
14298 CE_TOUCHING_X, border_side);
14300 #if USE_FIX_CE_ACTION_WITH_PLAYER
14301 if (IS_PLAYER(xx, yy))
14303 /* use player element that is initially defined in the level playfield,
14304 not the player element that corresponds to the runtime player number
14305 (example: a level that contains EL_PLAYER_3 as the only player would
14306 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14307 int player_element = PLAYERINFO(xx, yy)->initial_element;
14309 CheckElementChangeBySide(x, y, center_element, player_element,
14310 CE_TOUCHING_X, border_side);
14318 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14320 static int xy[4][2] =
14327 static int trigger_sides[4][2] =
14329 /* center side border side */
14330 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14331 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14332 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14333 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14335 static int touch_dir[4] =
14337 MV_LEFT | MV_RIGHT,
14342 boolean change_center_element = FALSE;
14343 int center_element = Feld[x][y]; /* should always be non-moving! */
14346 for (i = 0; i < NUM_DIRECTIONS; i++)
14348 int xx = x + xy[i][0];
14349 int yy = y + xy[i][1];
14350 int center_side = trigger_sides[i][0];
14351 int border_side = trigger_sides[i][1];
14352 int border_element;
14354 if (!IN_LEV_FIELD(xx, yy))
14357 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14358 border_element = Feld[xx][yy]; /* may be moving! */
14359 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14360 border_element = Feld[xx][yy];
14361 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14362 border_element = MovingOrBlocked2Element(xx, yy);
14364 continue; /* center and border element do not touch */
14366 /* check for change of center element (but change it only once) */
14367 if (!change_center_element)
14368 change_center_element =
14369 CheckElementChangeBySide(x, y, center_element, border_element,
14370 CE_TOUCHING_X, border_side);
14372 /* check for change of border element */
14373 CheckElementChangeBySide(xx, yy, border_element, center_element,
14374 CE_TOUCHING_X, center_side);
14380 void TestIfElementHitsCustomElement(int x, int y, int direction)
14382 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14383 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14384 int hitx = x + dx, hity = y + dy;
14385 int hitting_element = Feld[x][y];
14386 int touched_element;
14388 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14391 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14392 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14394 if (IN_LEV_FIELD(hitx, hity))
14396 int opposite_direction = MV_DIR_OPPOSITE(direction);
14397 int hitting_side = direction;
14398 int touched_side = opposite_direction;
14399 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14400 MovDir[hitx][hity] != direction ||
14401 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14407 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14408 CE_HITTING_X, touched_side);
14410 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14411 CE_HIT_BY_X, hitting_side);
14413 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14414 CE_HIT_BY_SOMETHING, opposite_direction);
14416 #if USE_FIX_CE_ACTION_WITH_PLAYER
14417 if (IS_PLAYER(hitx, hity))
14419 /* use player element that is initially defined in the level playfield,
14420 not the player element that corresponds to the runtime player number
14421 (example: a level that contains EL_PLAYER_3 as the only player would
14422 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14423 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14425 CheckElementChangeBySide(x, y, hitting_element, player_element,
14426 CE_HITTING_X, touched_side);
14432 /* "hitting something" is also true when hitting the playfield border */
14433 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14434 CE_HITTING_SOMETHING, direction);
14438 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14440 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14441 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14442 int hitx = x + dx, hity = y + dy;
14443 int hitting_element = Feld[x][y];
14444 int touched_element;
14446 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14447 !IS_FREE(hitx, hity) &&
14448 (!IS_MOVING(hitx, hity) ||
14449 MovDir[hitx][hity] != direction ||
14450 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14453 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14457 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14461 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14462 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14464 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14465 EP_CAN_SMASH_EVERYTHING, direction);
14467 if (IN_LEV_FIELD(hitx, hity))
14469 int opposite_direction = MV_DIR_OPPOSITE(direction);
14470 int hitting_side = direction;
14471 int touched_side = opposite_direction;
14473 int touched_element = MovingOrBlocked2Element(hitx, hity);
14476 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14477 MovDir[hitx][hity] != direction ||
14478 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14487 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14488 CE_SMASHED_BY_SOMETHING, opposite_direction);
14490 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14491 CE_OTHER_IS_SMASHING, touched_side);
14493 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14494 CE_OTHER_GETS_SMASHED, hitting_side);
14500 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14502 int i, kill_x = -1, kill_y = -1;
14504 int bad_element = -1;
14505 static int test_xy[4][2] =
14512 static int test_dir[4] =
14520 for (i = 0; i < NUM_DIRECTIONS; i++)
14522 int test_x, test_y, test_move_dir, test_element;
14524 test_x = good_x + test_xy[i][0];
14525 test_y = good_y + test_xy[i][1];
14527 if (!IN_LEV_FIELD(test_x, test_y))
14531 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14533 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14535 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14536 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14538 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14539 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14543 bad_element = test_element;
14549 if (kill_x != -1 || kill_y != -1)
14551 if (IS_PLAYER(good_x, good_y))
14553 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14555 if (player->shield_deadly_time_left > 0 &&
14556 !IS_INDESTRUCTIBLE(bad_element))
14557 Bang(kill_x, kill_y);
14558 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14559 KillPlayer(player);
14562 Bang(good_x, good_y);
14566 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14568 int i, kill_x = -1, kill_y = -1;
14569 int bad_element = Feld[bad_x][bad_y];
14570 static int test_xy[4][2] =
14577 static int touch_dir[4] =
14579 MV_LEFT | MV_RIGHT,
14584 static int test_dir[4] =
14592 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14595 for (i = 0; i < NUM_DIRECTIONS; i++)
14597 int test_x, test_y, test_move_dir, test_element;
14599 test_x = bad_x + test_xy[i][0];
14600 test_y = bad_y + test_xy[i][1];
14602 if (!IN_LEV_FIELD(test_x, test_y))
14606 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14608 test_element = Feld[test_x][test_y];
14610 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14611 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14613 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14614 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14616 /* good thing is player or penguin that does not move away */
14617 if (IS_PLAYER(test_x, test_y))
14619 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14621 if (bad_element == EL_ROBOT && player->is_moving)
14622 continue; /* robot does not kill player if he is moving */
14624 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14626 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14627 continue; /* center and border element do not touch */
14635 else if (test_element == EL_PENGUIN)
14645 if (kill_x != -1 || kill_y != -1)
14647 if (IS_PLAYER(kill_x, kill_y))
14649 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14651 if (player->shield_deadly_time_left > 0 &&
14652 !IS_INDESTRUCTIBLE(bad_element))
14653 Bang(bad_x, bad_y);
14654 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14655 KillPlayer(player);
14658 Bang(kill_x, kill_y);
14662 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14664 int bad_element = Feld[bad_x][bad_y];
14665 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14666 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14667 int test_x = bad_x + dx, test_y = bad_y + dy;
14668 int test_move_dir, test_element;
14669 int kill_x = -1, kill_y = -1;
14671 if (!IN_LEV_FIELD(test_x, test_y))
14675 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14677 test_element = Feld[test_x][test_y];
14679 if (test_move_dir != bad_move_dir)
14681 /* good thing can be player or penguin that does not move away */
14682 if (IS_PLAYER(test_x, test_y))
14684 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14686 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14687 player as being hit when he is moving towards the bad thing, because
14688 the "get hit by" condition would be lost after the player stops) */
14689 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14690 return; /* player moves away from bad thing */
14695 else if (test_element == EL_PENGUIN)
14702 if (kill_x != -1 || kill_y != -1)
14704 if (IS_PLAYER(kill_x, kill_y))
14706 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14708 if (player->shield_deadly_time_left > 0 &&
14709 !IS_INDESTRUCTIBLE(bad_element))
14710 Bang(bad_x, bad_y);
14711 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14712 KillPlayer(player);
14715 Bang(kill_x, kill_y);
14719 void TestIfPlayerTouchesBadThing(int x, int y)
14721 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14724 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14726 TestIfGoodThingHitsBadThing(x, y, move_dir);
14729 void TestIfBadThingTouchesPlayer(int x, int y)
14731 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14734 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14736 TestIfBadThingHitsGoodThing(x, y, move_dir);
14739 void TestIfFriendTouchesBadThing(int x, int y)
14741 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14744 void TestIfBadThingTouchesFriend(int x, int y)
14746 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14749 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14751 int i, kill_x = bad_x, kill_y = bad_y;
14752 static int xy[4][2] =
14760 for (i = 0; i < NUM_DIRECTIONS; i++)
14764 x = bad_x + xy[i][0];
14765 y = bad_y + xy[i][1];
14766 if (!IN_LEV_FIELD(x, y))
14769 element = Feld[x][y];
14770 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14771 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14779 if (kill_x != bad_x || kill_y != bad_y)
14780 Bang(bad_x, bad_y);
14783 void KillPlayer(struct PlayerInfo *player)
14785 int jx = player->jx, jy = player->jy;
14787 if (!player->active)
14791 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14792 player->killed, player->active, player->reanimated);
14795 /* the following code was introduced to prevent an infinite loop when calling
14797 -> CheckTriggeredElementChangeExt()
14798 -> ExecuteCustomElementAction()
14800 -> (infinitely repeating the above sequence of function calls)
14801 which occurs when killing the player while having a CE with the setting
14802 "kill player X when explosion of <player X>"; the solution using a new
14803 field "player->killed" was chosen for backwards compatibility, although
14804 clever use of the fields "player->active" etc. would probably also work */
14806 if (player->killed)
14810 player->killed = TRUE;
14812 /* remove accessible field at the player's position */
14813 Feld[jx][jy] = EL_EMPTY;
14815 /* deactivate shield (else Bang()/Explode() would not work right) */
14816 player->shield_normal_time_left = 0;
14817 player->shield_deadly_time_left = 0;
14820 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14821 player->killed, player->active, player->reanimated);
14827 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14828 player->killed, player->active, player->reanimated);
14831 #if USE_PLAYER_REANIMATION
14833 if (player->reanimated) /* killed player may have been reanimated */
14834 player->killed = player->reanimated = FALSE;
14836 BuryPlayer(player);
14838 if (player->killed) /* player may have been reanimated */
14839 BuryPlayer(player);
14842 BuryPlayer(player);
14846 static void KillPlayerUnlessEnemyProtected(int x, int y)
14848 if (!PLAYER_ENEMY_PROTECTED(x, y))
14849 KillPlayer(PLAYERINFO(x, y));
14852 static void KillPlayerUnlessExplosionProtected(int x, int y)
14854 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14855 KillPlayer(PLAYERINFO(x, y));
14858 void BuryPlayer(struct PlayerInfo *player)
14860 int jx = player->jx, jy = player->jy;
14862 if (!player->active)
14865 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14866 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14868 player->GameOver = TRUE;
14869 RemovePlayer(player);
14872 void RemovePlayer(struct PlayerInfo *player)
14874 int jx = player->jx, jy = player->jy;
14875 int i, found = FALSE;
14877 player->present = FALSE;
14878 player->active = FALSE;
14880 if (!ExplodeField[jx][jy])
14881 StorePlayer[jx][jy] = 0;
14883 if (player->is_moving)
14884 TEST_DrawLevelField(player->last_jx, player->last_jy);
14886 for (i = 0; i < MAX_PLAYERS; i++)
14887 if (stored_player[i].active)
14891 AllPlayersGone = TRUE;
14897 #if USE_NEW_SNAP_DELAY
14898 static void setFieldForSnapping(int x, int y, int element, int direction)
14900 struct ElementInfo *ei = &element_info[element];
14901 int direction_bit = MV_DIR_TO_BIT(direction);
14902 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14903 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14904 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14906 Feld[x][y] = EL_ELEMENT_SNAPPING;
14907 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14909 ResetGfxAnimation(x, y);
14911 GfxElement[x][y] = element;
14912 GfxAction[x][y] = action;
14913 GfxDir[x][y] = direction;
14914 GfxFrame[x][y] = -1;
14919 =============================================================================
14920 checkDiagonalPushing()
14921 -----------------------------------------------------------------------------
14922 check if diagonal input device direction results in pushing of object
14923 (by checking if the alternative direction is walkable, diggable, ...)
14924 =============================================================================
14927 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14928 int x, int y, int real_dx, int real_dy)
14930 int jx, jy, dx, dy, xx, yy;
14932 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14935 /* diagonal direction: check alternative direction */
14940 xx = jx + (dx == 0 ? real_dx : 0);
14941 yy = jy + (dy == 0 ? real_dy : 0);
14943 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14947 =============================================================================
14949 -----------------------------------------------------------------------------
14950 x, y: field next to player (non-diagonal) to try to dig to
14951 real_dx, real_dy: direction as read from input device (can be diagonal)
14952 =============================================================================
14955 static int DigField(struct PlayerInfo *player,
14956 int oldx, int oldy, int x, int y,
14957 int real_dx, int real_dy, int mode)
14959 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14960 boolean player_was_pushing = player->is_pushing;
14961 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14962 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14963 int jx = oldx, jy = oldy;
14964 int dx = x - jx, dy = y - jy;
14965 int nextx = x + dx, nexty = y + dy;
14966 int move_direction = (dx == -1 ? MV_LEFT :
14967 dx == +1 ? MV_RIGHT :
14969 dy == +1 ? MV_DOWN : MV_NONE);
14970 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14971 int dig_side = MV_DIR_OPPOSITE(move_direction);
14972 int old_element = Feld[jx][jy];
14973 #if USE_FIXED_DONT_RUN_INTO
14974 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14980 if (is_player) /* function can also be called by EL_PENGUIN */
14982 if (player->MovPos == 0)
14984 player->is_digging = FALSE;
14985 player->is_collecting = FALSE;
14988 if (player->MovPos == 0) /* last pushing move finished */
14989 player->is_pushing = FALSE;
14991 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14993 player->is_switching = FALSE;
14994 player->push_delay = -1;
14996 return MP_NO_ACTION;
15000 #if !USE_FIXED_DONT_RUN_INTO
15001 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15002 return MP_NO_ACTION;
15005 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
15006 old_element = Back[jx][jy];
15008 /* in case of element dropped at player position, check background */
15009 else if (Back[jx][jy] != EL_EMPTY &&
15010 game.engine_version >= VERSION_IDENT(2,2,0,0))
15011 old_element = Back[jx][jy];
15013 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
15014 return MP_NO_ACTION; /* field has no opening in this direction */
15016 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
15017 return MP_NO_ACTION; /* field has no opening in this direction */
15019 #if USE_FIXED_DONT_RUN_INTO
15020 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
15024 Feld[jx][jy] = player->artwork_element;
15025 InitMovingField(jx, jy, MV_DOWN);
15026 Store[jx][jy] = EL_ACID;
15027 ContinueMoving(jx, jy);
15028 BuryPlayer(player);
15030 return MP_DONT_RUN_INTO;
15034 #if USE_FIXED_DONT_RUN_INTO
15035 if (player_can_move && DONT_RUN_INTO(element))
15037 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
15039 return MP_DONT_RUN_INTO;
15043 #if USE_FIXED_DONT_RUN_INTO
15044 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
15045 return MP_NO_ACTION;
15048 #if !USE_FIXED_DONT_RUN_INTO
15049 element = Feld[x][y];
15052 collect_count = element_info[element].collect_count_initial;
15054 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
15055 return MP_NO_ACTION;
15057 if (game.engine_version < VERSION_IDENT(2,2,0,0))
15058 player_can_move = player_can_move_or_snap;
15060 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15061 game.engine_version >= VERSION_IDENT(2,2,0,0))
15063 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15064 player->index_bit, dig_side);
15065 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15066 player->index_bit, dig_side);
15068 if (element == EL_DC_LANDMINE)
15071 if (Feld[x][y] != element) /* field changed by snapping */
15074 return MP_NO_ACTION;
15077 #if USE_PLAYER_GRAVITY
15078 if (player->gravity && is_player && !player->is_auto_moving &&
15079 canFallDown(player) && move_direction != MV_DOWN &&
15080 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15081 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15083 if (game.gravity && is_player && !player->is_auto_moving &&
15084 canFallDown(player) && move_direction != MV_DOWN &&
15085 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15086 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15089 if (player_can_move &&
15090 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15092 int sound_element = SND_ELEMENT(element);
15093 int sound_action = ACTION_WALKING;
15095 if (IS_RND_GATE(element))
15097 if (!player->key[RND_GATE_NR(element)])
15098 return MP_NO_ACTION;
15100 else if (IS_RND_GATE_GRAY(element))
15102 if (!player->key[RND_GATE_GRAY_NR(element)])
15103 return MP_NO_ACTION;
15105 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15107 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15108 return MP_NO_ACTION;
15110 else if (element == EL_EXIT_OPEN ||
15111 element == EL_EM_EXIT_OPEN ||
15113 element == EL_EM_EXIT_OPENING ||
15115 element == EL_STEEL_EXIT_OPEN ||
15116 element == EL_EM_STEEL_EXIT_OPEN ||
15118 element == EL_EM_STEEL_EXIT_OPENING ||
15120 element == EL_SP_EXIT_OPEN ||
15121 element == EL_SP_EXIT_OPENING)
15123 sound_action = ACTION_PASSING; /* player is passing exit */
15125 else if (element == EL_EMPTY)
15127 sound_action = ACTION_MOVING; /* nothing to walk on */
15130 /* play sound from background or player, whatever is available */
15131 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15132 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15134 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15136 else if (player_can_move &&
15137 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15139 if (!ACCESS_FROM(element, opposite_direction))
15140 return MP_NO_ACTION; /* field not accessible from this direction */
15142 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15143 return MP_NO_ACTION;
15145 if (IS_EM_GATE(element))
15147 if (!player->key[EM_GATE_NR(element)])
15148 return MP_NO_ACTION;
15150 else if (IS_EM_GATE_GRAY(element))
15152 if (!player->key[EM_GATE_GRAY_NR(element)])
15153 return MP_NO_ACTION;
15155 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15157 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15158 return MP_NO_ACTION;
15160 else if (IS_EMC_GATE(element))
15162 if (!player->key[EMC_GATE_NR(element)])
15163 return MP_NO_ACTION;
15165 else if (IS_EMC_GATE_GRAY(element))
15167 if (!player->key[EMC_GATE_GRAY_NR(element)])
15168 return MP_NO_ACTION;
15170 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15172 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15173 return MP_NO_ACTION;
15175 else if (element == EL_DC_GATE_WHITE ||
15176 element == EL_DC_GATE_WHITE_GRAY ||
15177 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15179 if (player->num_white_keys == 0)
15180 return MP_NO_ACTION;
15182 player->num_white_keys--;
15184 else if (IS_SP_PORT(element))
15186 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15187 element == EL_SP_GRAVITY_PORT_RIGHT ||
15188 element == EL_SP_GRAVITY_PORT_UP ||
15189 element == EL_SP_GRAVITY_PORT_DOWN)
15190 #if USE_PLAYER_GRAVITY
15191 player->gravity = !player->gravity;
15193 game.gravity = !game.gravity;
15195 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15196 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15197 element == EL_SP_GRAVITY_ON_PORT_UP ||
15198 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15199 #if USE_PLAYER_GRAVITY
15200 player->gravity = TRUE;
15202 game.gravity = TRUE;
15204 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15205 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15206 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15207 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15208 #if USE_PLAYER_GRAVITY
15209 player->gravity = FALSE;
15211 game.gravity = FALSE;
15215 /* automatically move to the next field with double speed */
15216 player->programmed_action = move_direction;
15218 if (player->move_delay_reset_counter == 0)
15220 player->move_delay_reset_counter = 2; /* two double speed steps */
15222 DOUBLE_PLAYER_SPEED(player);
15225 PlayLevelSoundAction(x, y, ACTION_PASSING);
15227 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15231 if (mode != DF_SNAP)
15233 GfxElement[x][y] = GFX_ELEMENT(element);
15234 player->is_digging = TRUE;
15237 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15239 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15240 player->index_bit, dig_side);
15242 if (mode == DF_SNAP)
15244 #if USE_NEW_SNAP_DELAY
15245 if (level.block_snap_field)
15246 setFieldForSnapping(x, y, element, move_direction);
15248 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15250 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15253 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15254 player->index_bit, dig_side);
15257 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15261 if (is_player && mode != DF_SNAP)
15263 GfxElement[x][y] = element;
15264 player->is_collecting = TRUE;
15267 if (element == EL_SPEED_PILL)
15269 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15271 else if (element == EL_EXTRA_TIME && level.time > 0)
15273 TimeLeft += level.extra_time;
15276 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15278 DisplayGameControlValues();
15280 DrawGameValue_Time(TimeLeft);
15283 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15285 player->shield_normal_time_left += level.shield_normal_time;
15286 if (element == EL_SHIELD_DEADLY)
15287 player->shield_deadly_time_left += level.shield_deadly_time;
15289 else if (element == EL_DYNAMITE ||
15290 element == EL_EM_DYNAMITE ||
15291 element == EL_SP_DISK_RED)
15293 if (player->inventory_size < MAX_INVENTORY_SIZE)
15294 player->inventory_element[player->inventory_size++] = element;
15296 DrawGameDoorValues();
15298 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15300 player->dynabomb_count++;
15301 player->dynabombs_left++;
15303 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15305 player->dynabomb_size++;
15307 else if (element == EL_DYNABOMB_INCREASE_POWER)
15309 player->dynabomb_xl = TRUE;
15311 else if (IS_KEY(element))
15313 player->key[KEY_NR(element)] = TRUE;
15315 DrawGameDoorValues();
15317 else if (element == EL_DC_KEY_WHITE)
15319 player->num_white_keys++;
15321 /* display white keys? */
15322 /* DrawGameDoorValues(); */
15324 else if (IS_ENVELOPE(element))
15326 player->show_envelope = element;
15328 else if (element == EL_EMC_LENSES)
15330 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15332 RedrawAllInvisibleElementsForLenses();
15334 else if (element == EL_EMC_MAGNIFIER)
15336 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15338 RedrawAllInvisibleElementsForMagnifier();
15340 else if (IS_DROPPABLE(element) ||
15341 IS_THROWABLE(element)) /* can be collected and dropped */
15345 if (collect_count == 0)
15346 player->inventory_infinite_element = element;
15348 for (i = 0; i < collect_count; i++)
15349 if (player->inventory_size < MAX_INVENTORY_SIZE)
15350 player->inventory_element[player->inventory_size++] = element;
15352 DrawGameDoorValues();
15354 else if (collect_count > 0)
15356 local_player->gems_still_needed -= collect_count;
15357 if (local_player->gems_still_needed < 0)
15358 local_player->gems_still_needed = 0;
15361 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15363 DisplayGameControlValues();
15365 DrawGameValue_Emeralds(local_player->gems_still_needed);
15369 RaiseScoreElement(element);
15370 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15373 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15374 player->index_bit, dig_side);
15376 if (mode == DF_SNAP)
15378 #if USE_NEW_SNAP_DELAY
15379 if (level.block_snap_field)
15380 setFieldForSnapping(x, y, element, move_direction);
15382 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15384 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15387 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15388 player->index_bit, dig_side);
15391 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15393 if (mode == DF_SNAP && element != EL_BD_ROCK)
15394 return MP_NO_ACTION;
15396 if (CAN_FALL(element) && dy)
15397 return MP_NO_ACTION;
15399 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15400 !(element == EL_SPRING && level.use_spring_bug))
15401 return MP_NO_ACTION;
15403 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15404 ((move_direction & MV_VERTICAL &&
15405 ((element_info[element].move_pattern & MV_LEFT &&
15406 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15407 (element_info[element].move_pattern & MV_RIGHT &&
15408 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15409 (move_direction & MV_HORIZONTAL &&
15410 ((element_info[element].move_pattern & MV_UP &&
15411 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15412 (element_info[element].move_pattern & MV_DOWN &&
15413 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15414 return MP_NO_ACTION;
15416 /* do not push elements already moving away faster than player */
15417 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15418 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15419 return MP_NO_ACTION;
15421 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15423 if (player->push_delay_value == -1 || !player_was_pushing)
15424 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15426 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15428 if (player->push_delay_value == -1)
15429 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15431 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15433 if (!player->is_pushing)
15434 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15437 player->is_pushing = TRUE;
15438 player->is_active = TRUE;
15440 if (!(IN_LEV_FIELD(nextx, nexty) &&
15441 (IS_FREE(nextx, nexty) ||
15442 (IS_SB_ELEMENT(element) &&
15443 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15444 (IS_CUSTOM_ELEMENT(element) &&
15445 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15446 return MP_NO_ACTION;
15448 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15449 return MP_NO_ACTION;
15451 if (player->push_delay == -1) /* new pushing; restart delay */
15452 player->push_delay = 0;
15454 if (player->push_delay < player->push_delay_value &&
15455 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15456 element != EL_SPRING && element != EL_BALLOON)
15458 /* make sure that there is no move delay before next try to push */
15459 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15460 player->move_delay = 0;
15462 return MP_NO_ACTION;
15465 if (IS_CUSTOM_ELEMENT(element) &&
15466 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15468 if (!DigFieldByCE(nextx, nexty, element))
15469 return MP_NO_ACTION;
15472 if (IS_SB_ELEMENT(element))
15474 if (element == EL_SOKOBAN_FIELD_FULL)
15476 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15477 local_player->sokobanfields_still_needed++;
15480 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15482 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15483 local_player->sokobanfields_still_needed--;
15486 Feld[x][y] = EL_SOKOBAN_OBJECT;
15488 if (Back[x][y] == Back[nextx][nexty])
15489 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15490 else if (Back[x][y] != 0)
15491 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15494 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15498 if (local_player->sokobanfields_still_needed == 0 &&
15499 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15501 if (local_player->sokobanfields_still_needed == 0 &&
15502 game.emulation == EMU_SOKOBAN)
15505 PlayerWins(player);
15507 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15511 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15513 InitMovingField(x, y, move_direction);
15514 GfxAction[x][y] = ACTION_PUSHING;
15516 if (mode == DF_SNAP)
15517 ContinueMoving(x, y);
15519 MovPos[x][y] = (dx != 0 ? dx : dy);
15521 Pushed[x][y] = TRUE;
15522 Pushed[nextx][nexty] = TRUE;
15524 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15525 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15527 player->push_delay_value = -1; /* get new value later */
15529 /* check for element change _after_ element has been pushed */
15530 if (game.use_change_when_pushing_bug)
15532 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15533 player->index_bit, dig_side);
15534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15535 player->index_bit, dig_side);
15538 else if (IS_SWITCHABLE(element))
15540 if (PLAYER_SWITCHING(player, x, y))
15542 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15543 player->index_bit, dig_side);
15548 player->is_switching = TRUE;
15549 player->switch_x = x;
15550 player->switch_y = y;
15552 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15554 if (element == EL_ROBOT_WHEEL)
15556 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15560 game.robot_wheel_active = TRUE;
15562 TEST_DrawLevelField(x, y);
15564 else if (element == EL_SP_TERMINAL)
15568 SCAN_PLAYFIELD(xx, yy)
15570 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15572 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15573 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15576 else if (IS_BELT_SWITCH(element))
15578 ToggleBeltSwitch(x, y);
15580 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15581 element == EL_SWITCHGATE_SWITCH_DOWN ||
15582 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15583 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15585 ToggleSwitchgateSwitch(x, y);
15587 else if (element == EL_LIGHT_SWITCH ||
15588 element == EL_LIGHT_SWITCH_ACTIVE)
15590 ToggleLightSwitch(x, y);
15592 else if (element == EL_TIMEGATE_SWITCH ||
15593 element == EL_DC_TIMEGATE_SWITCH)
15595 ActivateTimegateSwitch(x, y);
15597 else if (element == EL_BALLOON_SWITCH_LEFT ||
15598 element == EL_BALLOON_SWITCH_RIGHT ||
15599 element == EL_BALLOON_SWITCH_UP ||
15600 element == EL_BALLOON_SWITCH_DOWN ||
15601 element == EL_BALLOON_SWITCH_NONE ||
15602 element == EL_BALLOON_SWITCH_ANY)
15604 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15605 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15606 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15607 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15608 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15611 else if (element == EL_LAMP)
15613 Feld[x][y] = EL_LAMP_ACTIVE;
15614 local_player->lights_still_needed--;
15616 ResetGfxAnimation(x, y);
15617 TEST_DrawLevelField(x, y);
15619 else if (element == EL_TIME_ORB_FULL)
15621 Feld[x][y] = EL_TIME_ORB_EMPTY;
15623 if (level.time > 0 || level.use_time_orb_bug)
15625 TimeLeft += level.time_orb_time;
15628 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15630 DisplayGameControlValues();
15632 DrawGameValue_Time(TimeLeft);
15636 ResetGfxAnimation(x, y);
15637 TEST_DrawLevelField(x, y);
15639 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15640 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15644 game.ball_state = !game.ball_state;
15646 SCAN_PLAYFIELD(xx, yy)
15648 int e = Feld[xx][yy];
15650 if (game.ball_state)
15652 if (e == EL_EMC_MAGIC_BALL)
15653 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15654 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15655 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15659 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15660 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15661 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15662 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15667 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15668 player->index_bit, dig_side);
15670 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15671 player->index_bit, dig_side);
15673 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15674 player->index_bit, dig_side);
15680 if (!PLAYER_SWITCHING(player, x, y))
15682 player->is_switching = TRUE;
15683 player->switch_x = x;
15684 player->switch_y = y;
15686 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15687 player->index_bit, dig_side);
15688 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15689 player->index_bit, dig_side);
15691 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15692 player->index_bit, dig_side);
15693 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15694 player->index_bit, dig_side);
15697 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15698 player->index_bit, dig_side);
15699 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15700 player->index_bit, dig_side);
15702 return MP_NO_ACTION;
15705 player->push_delay = -1;
15707 if (is_player) /* function can also be called by EL_PENGUIN */
15709 if (Feld[x][y] != element) /* really digged/collected something */
15711 player->is_collecting = !player->is_digging;
15712 player->is_active = TRUE;
15719 static boolean DigFieldByCE(int x, int y, int digging_element)
15721 int element = Feld[x][y];
15723 if (!IS_FREE(x, y))
15725 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15726 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15729 /* no element can dig solid indestructible elements */
15730 if (IS_INDESTRUCTIBLE(element) &&
15731 !IS_DIGGABLE(element) &&
15732 !IS_COLLECTIBLE(element))
15735 if (AmoebaNr[x][y] &&
15736 (element == EL_AMOEBA_FULL ||
15737 element == EL_BD_AMOEBA ||
15738 element == EL_AMOEBA_GROWING))
15740 AmoebaCnt[AmoebaNr[x][y]]--;
15741 AmoebaCnt2[AmoebaNr[x][y]]--;
15744 if (IS_MOVING(x, y))
15745 RemoveMovingField(x, y);
15749 TEST_DrawLevelField(x, y);
15752 /* if digged element was about to explode, prevent the explosion */
15753 ExplodeField[x][y] = EX_TYPE_NONE;
15755 PlayLevelSoundAction(x, y, action);
15758 Store[x][y] = EL_EMPTY;
15761 /* this makes it possible to leave the removed element again */
15762 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15763 Store[x][y] = element;
15765 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15767 int move_leave_element = element_info[digging_element].move_leave_element;
15769 /* this makes it possible to leave the removed element again */
15770 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15771 element : move_leave_element);
15778 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15780 int jx = player->jx, jy = player->jy;
15781 int x = jx + dx, y = jy + dy;
15782 int snap_direction = (dx == -1 ? MV_LEFT :
15783 dx == +1 ? MV_RIGHT :
15785 dy == +1 ? MV_DOWN : MV_NONE);
15786 boolean can_continue_snapping = (level.continuous_snapping &&
15787 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15789 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15792 if (!player->active || !IN_LEV_FIELD(x, y))
15800 if (player->MovPos == 0)
15801 player->is_pushing = FALSE;
15803 player->is_snapping = FALSE;
15805 if (player->MovPos == 0)
15807 player->is_moving = FALSE;
15808 player->is_digging = FALSE;
15809 player->is_collecting = FALSE;
15815 #if USE_NEW_CONTINUOUS_SNAPPING
15816 /* prevent snapping with already pressed snap key when not allowed */
15817 if (player->is_snapping && !can_continue_snapping)
15820 if (player->is_snapping)
15824 player->MovDir = snap_direction;
15826 if (player->MovPos == 0)
15828 player->is_moving = FALSE;
15829 player->is_digging = FALSE;
15830 player->is_collecting = FALSE;
15833 player->is_dropping = FALSE;
15834 player->is_dropping_pressed = FALSE;
15835 player->drop_pressed_delay = 0;
15837 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15840 player->is_snapping = TRUE;
15841 player->is_active = TRUE;
15843 if (player->MovPos == 0)
15845 player->is_moving = FALSE;
15846 player->is_digging = FALSE;
15847 player->is_collecting = FALSE;
15850 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15851 TEST_DrawLevelField(player->last_jx, player->last_jy);
15853 TEST_DrawLevelField(x, y);
15858 static boolean DropElement(struct PlayerInfo *player)
15860 int old_element, new_element;
15861 int dropx = player->jx, dropy = player->jy;
15862 int drop_direction = player->MovDir;
15863 int drop_side = drop_direction;
15865 int drop_element = get_next_dropped_element(player);
15867 int drop_element = (player->inventory_size > 0 ?
15868 player->inventory_element[player->inventory_size - 1] :
15869 player->inventory_infinite_element != EL_UNDEFINED ?
15870 player->inventory_infinite_element :
15871 player->dynabombs_left > 0 ?
15872 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15876 player->is_dropping_pressed = TRUE;
15878 /* do not drop an element on top of another element; when holding drop key
15879 pressed without moving, dropped element must move away before the next
15880 element can be dropped (this is especially important if the next element
15881 is dynamite, which can be placed on background for historical reasons) */
15882 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15885 if (IS_THROWABLE(drop_element))
15887 dropx += GET_DX_FROM_DIR(drop_direction);
15888 dropy += GET_DY_FROM_DIR(drop_direction);
15890 if (!IN_LEV_FIELD(dropx, dropy))
15894 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15895 new_element = drop_element; /* default: no change when dropping */
15897 /* check if player is active, not moving and ready to drop */
15898 if (!player->active || player->MovPos || player->drop_delay > 0)
15901 /* check if player has anything that can be dropped */
15902 if (new_element == EL_UNDEFINED)
15905 /* check if drop key was pressed long enough for EM style dynamite */
15906 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15909 /* check if anything can be dropped at the current position */
15910 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15913 /* collected custom elements can only be dropped on empty fields */
15914 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15917 if (old_element != EL_EMPTY)
15918 Back[dropx][dropy] = old_element; /* store old element on this field */
15920 ResetGfxAnimation(dropx, dropy);
15921 ResetRandomAnimationValue(dropx, dropy);
15923 if (player->inventory_size > 0 ||
15924 player->inventory_infinite_element != EL_UNDEFINED)
15926 if (player->inventory_size > 0)
15928 player->inventory_size--;
15930 DrawGameDoorValues();
15932 if (new_element == EL_DYNAMITE)
15933 new_element = EL_DYNAMITE_ACTIVE;
15934 else if (new_element == EL_EM_DYNAMITE)
15935 new_element = EL_EM_DYNAMITE_ACTIVE;
15936 else if (new_element == EL_SP_DISK_RED)
15937 new_element = EL_SP_DISK_RED_ACTIVE;
15940 Feld[dropx][dropy] = new_element;
15942 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15943 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15944 el2img(Feld[dropx][dropy]), 0);
15946 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15948 /* needed if previous element just changed to "empty" in the last frame */
15949 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15951 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15952 player->index_bit, drop_side);
15953 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15955 player->index_bit, drop_side);
15957 TestIfElementTouchesCustomElement(dropx, dropy);
15959 else /* player is dropping a dyna bomb */
15961 player->dynabombs_left--;
15963 Feld[dropx][dropy] = new_element;
15965 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15966 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15967 el2img(Feld[dropx][dropy]), 0);
15969 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15972 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15973 InitField_WithBug1(dropx, dropy, FALSE);
15975 new_element = Feld[dropx][dropy]; /* element might have changed */
15977 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15978 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15980 int move_direction, nextx, nexty;
15982 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15983 MovDir[dropx][dropy] = drop_direction;
15985 move_direction = MovDir[dropx][dropy];
15986 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15987 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15989 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15991 #if USE_FIX_IMPACT_COLLISION
15992 /* do not cause impact style collision by dropping elements that can fall */
15993 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15995 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15999 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
16000 player->is_dropping = TRUE;
16002 player->drop_pressed_delay = 0;
16003 player->is_dropping_pressed = FALSE;
16005 player->drop_x = dropx;
16006 player->drop_y = dropy;
16011 /* ------------------------------------------------------------------------- */
16012 /* game sound playing functions */
16013 /* ------------------------------------------------------------------------- */
16015 static int *loop_sound_frame = NULL;
16016 static int *loop_sound_volume = NULL;
16018 void InitPlayLevelSound()
16020 int num_sounds = getSoundListSize();
16022 checked_free(loop_sound_frame);
16023 checked_free(loop_sound_volume);
16025 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
16026 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
16029 static void PlayLevelSound(int x, int y, int nr)
16031 int sx = SCREENX(x), sy = SCREENY(y);
16032 int volume, stereo_position;
16033 int max_distance = 8;
16034 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
16036 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
16037 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
16040 if (!IN_LEV_FIELD(x, y) ||
16041 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
16042 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
16045 volume = SOUND_MAX_VOLUME;
16047 if (!IN_SCR_FIELD(sx, sy))
16049 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
16050 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
16052 volume -= volume * (dx > dy ? dx : dy) / max_distance;
16055 stereo_position = (SOUND_MAX_LEFT +
16056 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
16057 (SCR_FIELDX + 2 * max_distance));
16059 if (IS_LOOP_SOUND(nr))
16061 /* This assures that quieter loop sounds do not overwrite louder ones,
16062 while restarting sound volume comparison with each new game frame. */
16064 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16067 loop_sound_volume[nr] = volume;
16068 loop_sound_frame[nr] = FrameCounter;
16071 PlaySoundExt(nr, volume, stereo_position, type);
16074 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16076 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16077 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16078 y < LEVELY(BY1) ? LEVELY(BY1) :
16079 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16083 static void PlayLevelSoundAction(int x, int y, int action)
16085 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16088 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16090 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16092 if (sound_effect != SND_UNDEFINED)
16093 PlayLevelSound(x, y, sound_effect);
16096 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16099 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16101 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16102 PlayLevelSound(x, y, sound_effect);
16105 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16107 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16109 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16110 PlayLevelSound(x, y, sound_effect);
16113 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16115 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16117 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16118 StopSound(sound_effect);
16121 static void PlayLevelMusic()
16123 if (levelset.music[level_nr] != MUS_UNDEFINED)
16124 PlayMusic(levelset.music[level_nr]); /* from config file */
16126 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16129 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16131 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16132 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16133 int x = xx - 1 - offset;
16134 int y = yy - 1 - offset;
16139 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16143 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16147 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16151 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16155 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16159 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16163 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16166 case SAMPLE_android_clone:
16167 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16170 case SAMPLE_android_move:
16171 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16174 case SAMPLE_spring:
16175 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16179 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16183 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16186 case SAMPLE_eater_eat:
16187 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16191 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16194 case SAMPLE_collect:
16195 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16198 case SAMPLE_diamond:
16199 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16202 case SAMPLE_squash:
16203 /* !!! CHECK THIS !!! */
16205 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16207 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16211 case SAMPLE_wonderfall:
16212 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16216 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16220 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16224 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16228 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16232 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16236 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16239 case SAMPLE_wonder:
16240 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16244 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16247 case SAMPLE_exit_open:
16248 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16251 case SAMPLE_exit_leave:
16252 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16255 case SAMPLE_dynamite:
16256 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16260 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16264 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16268 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16272 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16276 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16280 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16284 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16289 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16291 int element = map_element_SP_to_RND(element_sp);
16292 int action = map_action_SP_to_RND(action_sp);
16293 int offset = (setup.sp_show_border_elements ? 0 : 1);
16294 int x = xx - offset;
16295 int y = yy - offset;
16298 printf("::: %d -> %d\n", element_sp, action_sp);
16301 PlayLevelSoundElementAction(x, y, element, action);
16305 void ChangeTime(int value)
16307 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16311 /* EMC game engine uses value from time counter of RND game engine */
16312 level.native_em_level->lev->time = *time;
16314 DrawGameValue_Time(*time);
16317 void RaiseScore(int value)
16319 /* EMC game engine and RND game engine have separate score counters */
16320 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16321 &level.native_em_level->lev->score : &local_player->score);
16325 DrawGameValue_Score(*score);
16329 void RaiseScore(int value)
16331 local_player->score += value;
16334 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16336 DisplayGameControlValues();
16338 DrawGameValue_Score(local_player->score);
16342 void RaiseScoreElement(int element)
16347 case EL_BD_DIAMOND:
16348 case EL_EMERALD_YELLOW:
16349 case EL_EMERALD_RED:
16350 case EL_EMERALD_PURPLE:
16351 case EL_SP_INFOTRON:
16352 RaiseScore(level.score[SC_EMERALD]);
16355 RaiseScore(level.score[SC_DIAMOND]);
16358 RaiseScore(level.score[SC_CRYSTAL]);
16361 RaiseScore(level.score[SC_PEARL]);
16364 case EL_BD_BUTTERFLY:
16365 case EL_SP_ELECTRON:
16366 RaiseScore(level.score[SC_BUG]);
16369 case EL_BD_FIREFLY:
16370 case EL_SP_SNIKSNAK:
16371 RaiseScore(level.score[SC_SPACESHIP]);
16374 case EL_DARK_YAMYAM:
16375 RaiseScore(level.score[SC_YAMYAM]);
16378 RaiseScore(level.score[SC_ROBOT]);
16381 RaiseScore(level.score[SC_PACMAN]);
16384 RaiseScore(level.score[SC_NUT]);
16387 case EL_EM_DYNAMITE:
16388 case EL_SP_DISK_RED:
16389 case EL_DYNABOMB_INCREASE_NUMBER:
16390 case EL_DYNABOMB_INCREASE_SIZE:
16391 case EL_DYNABOMB_INCREASE_POWER:
16392 RaiseScore(level.score[SC_DYNAMITE]);
16394 case EL_SHIELD_NORMAL:
16395 case EL_SHIELD_DEADLY:
16396 RaiseScore(level.score[SC_SHIELD]);
16398 case EL_EXTRA_TIME:
16399 RaiseScore(level.extra_time_score);
16413 case EL_DC_KEY_WHITE:
16414 RaiseScore(level.score[SC_KEY]);
16417 RaiseScore(element_info[element].collect_score);
16422 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16424 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16426 #if defined(NETWORK_AVALIABLE)
16427 if (options.network)
16428 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16437 FadeSkipNextFadeIn();
16439 fading = fading_none;
16443 OpenDoor(DOOR_CLOSE_1);
16446 game_status = GAME_MODE_MAIN;
16449 DrawAndFadeInMainMenu(REDRAW_FIELD);
16457 FadeOut(REDRAW_FIELD);
16460 game_status = GAME_MODE_MAIN;
16462 DrawAndFadeInMainMenu(REDRAW_FIELD);
16466 else /* continue playing the game */
16468 if (tape.playing && tape.deactivate_display)
16469 TapeDeactivateDisplayOff(TRUE);
16471 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16473 if (tape.playing && tape.deactivate_display)
16474 TapeDeactivateDisplayOn();
16478 void RequestQuitGame(boolean ask_if_really_quit)
16480 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16481 boolean skip_request = AllPlayersGone || quick_quit;
16483 RequestQuitGameExt(skip_request, quick_quit,
16484 "Do you really want to quit the game ?");
16488 /* ------------------------------------------------------------------------- */
16489 /* random generator functions */
16490 /* ------------------------------------------------------------------------- */
16492 unsigned int InitEngineRandom_RND(long seed)
16494 game.num_random_calls = 0;
16497 unsigned int rnd_seed = InitEngineRandom(seed);
16499 printf("::: START RND: %d\n", rnd_seed);
16504 return InitEngineRandom(seed);
16510 unsigned int RND(int max)
16514 game.num_random_calls++;
16516 return GetEngineRandom(max);
16523 /* ------------------------------------------------------------------------- */
16524 /* game engine snapshot handling functions */
16525 /* ------------------------------------------------------------------------- */
16527 struct EngineSnapshotInfo
16529 /* runtime values for custom element collect score */
16530 int collect_score[NUM_CUSTOM_ELEMENTS];
16532 /* runtime values for group element choice position */
16533 int choice_pos[NUM_GROUP_ELEMENTS];
16535 /* runtime values for belt position animations */
16536 int belt_graphic[4][NUM_BELT_PARTS];
16537 int belt_anim_mode[4][NUM_BELT_PARTS];
16540 static struct EngineSnapshotInfo engine_snapshot_rnd;
16541 static char *snapshot_level_identifier = NULL;
16542 static int snapshot_level_nr = -1;
16544 static void SaveEngineSnapshotValues_RND()
16546 static int belt_base_active_element[4] =
16548 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16549 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16550 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16551 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16555 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16557 int element = EL_CUSTOM_START + i;
16559 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16562 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16564 int element = EL_GROUP_START + i;
16566 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16569 for (i = 0; i < 4; i++)
16571 for (j = 0; j < NUM_BELT_PARTS; j++)
16573 int element = belt_base_active_element[i] + j;
16574 int graphic = el2img(element);
16575 int anim_mode = graphic_info[graphic].anim_mode;
16577 engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16578 engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16583 static void LoadEngineSnapshotValues_RND()
16585 unsigned long num_random_calls = game.num_random_calls;
16588 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16590 int element = EL_CUSTOM_START + i;
16592 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16595 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16597 int element = EL_GROUP_START + i;
16599 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16602 for (i = 0; i < 4; i++)
16604 for (j = 0; j < NUM_BELT_PARTS; j++)
16606 int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16607 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16609 graphic_info[graphic].anim_mode = anim_mode;
16613 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16615 InitRND(tape.random_seed);
16616 for (i = 0; i < num_random_calls; i++)
16620 if (game.num_random_calls != num_random_calls)
16622 Error(ERR_INFO, "number of random calls out of sync");
16623 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16624 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16625 Error(ERR_EXIT, "this should not happen -- please debug");
16629 void SaveEngineSnapshot()
16631 /* do not save snapshots from editor */
16632 if (level_editor_test_game)
16635 /* free previous snapshot buffers, if needed */
16636 FreeEngineSnapshotBuffers();
16638 /* copy some special values to a structure better suited for the snapshot */
16640 SaveEngineSnapshotValues_RND();
16641 SaveEngineSnapshotValues_EM();
16642 SaveEngineSnapshotValues_SP();
16644 /* save values stored in special snapshot structure */
16646 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16647 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16648 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16650 /* save further RND engine values */
16652 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16653 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16654 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16656 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16657 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16658 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16659 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16661 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16662 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16663 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16664 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16665 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16667 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16668 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16669 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16671 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16673 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16675 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16676 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16678 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16679 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16680 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16681 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16682 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16683 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16684 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16685 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16686 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16687 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16688 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16689 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16690 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16691 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16692 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16693 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16694 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16695 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16697 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16698 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16700 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16701 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16702 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16704 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16705 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16707 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16708 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16709 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16710 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16711 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16713 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16714 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16716 /* save level identification information */
16718 setString(&snapshot_level_identifier, leveldir_current->identifier);
16719 snapshot_level_nr = level_nr;
16722 ListNode *node = engine_snapshot_list_rnd;
16725 while (node != NULL)
16727 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16732 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16736 void LoadEngineSnapshot()
16738 /* restore generically stored snapshot buffers */
16740 LoadEngineSnapshotBuffers();
16742 /* restore special values from snapshot structure */
16744 LoadEngineSnapshotValues_RND();
16745 LoadEngineSnapshotValues_EM();
16746 LoadEngineSnapshotValues_SP();
16749 boolean CheckEngineSnapshot()
16751 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16752 snapshot_level_nr == level_nr);
16756 /* ---------- new game button stuff ---------------------------------------- */
16764 } gamebutton_info[NUM_GAME_BUTTONS] =
16767 IMG_GAME_BUTTON_GFX_STOP, &game.button.stop,
16768 GAME_CTRL_ID_STOP, "stop game"
16771 IMG_GAME_BUTTON_GFX_PAUSE, &game.button.pause,
16772 GAME_CTRL_ID_PAUSE, "pause game"
16775 IMG_GAME_BUTTON_GFX_PLAY, &game.button.play,
16776 GAME_CTRL_ID_PLAY, "play game"
16779 IMG_GAME_BUTTON_GFX_SOUND_MUSIC, &game.button.sound_music,
16780 SOUND_CTRL_ID_MUSIC, "background music on/off"
16783 IMG_GAME_BUTTON_GFX_SOUND_LOOPS, &game.button.sound_loops,
16784 SOUND_CTRL_ID_LOOPS, "sound loops on/off"
16787 IMG_GAME_BUTTON_GFX_SOUND_SIMPLE, &game.button.sound_simple,
16788 SOUND_CTRL_ID_SIMPLE, "normal sounds on/off"
16792 void CreateGameButtons()
16796 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16798 struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16799 struct Rect *pos = gamebutton_info[i].pos;
16800 struct GadgetInfo *gi;
16803 unsigned long event_mask;
16804 int gd_x = gfx->src_x;
16805 int gd_y = gfx->src_y;
16806 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
16807 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
16808 int gd_xa = gfx->src_x + gfx->active_xoffset;
16809 int gd_ya = gfx->src_y + gfx->active_yoffset;
16810 int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16811 int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16814 if (id == GAME_CTRL_ID_STOP ||
16815 id == GAME_CTRL_ID_PAUSE ||
16816 id == GAME_CTRL_ID_PLAY)
16818 button_type = GD_TYPE_NORMAL_BUTTON;
16820 event_mask = GD_EVENT_RELEASED;
16824 button_type = GD_TYPE_CHECK_BUTTON;
16826 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16827 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16828 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16829 event_mask = GD_EVENT_PRESSED;
16832 gi = CreateGadget(GDI_CUSTOM_ID, id,
16833 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16834 GDI_X, DX + pos->x,
16835 GDI_Y, DY + pos->y,
16836 GDI_WIDTH, gfx->width,
16837 GDI_HEIGHT, gfx->height,
16838 GDI_TYPE, button_type,
16839 GDI_STATE, GD_BUTTON_UNPRESSED,
16840 GDI_CHECKED, checked,
16841 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16842 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16843 GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16844 GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16845 GDI_DIRECT_DRAW, FALSE,
16846 GDI_EVENT_MASK, event_mask,
16847 GDI_CALLBACK_ACTION, HandleGameButtons,
16851 Error(ERR_EXIT, "cannot create gadget");
16853 game_gadget[id] = gi;
16857 void FreeGameButtons()
16861 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16862 FreeGadget(game_gadget[i]);
16865 static void MapGameButtons()
16869 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16870 MapGadget(game_gadget[i]);
16873 void UnmapGameButtons()
16877 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16878 UnmapGadget(game_gadget[i]);
16881 void RedrawGameButtons()
16885 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16886 RedrawGadget(game_gadget[i]);
16889 static void HandleGameButtonsExt(int id)
16891 if (game_status != GAME_MODE_PLAYING)
16896 case GAME_CTRL_ID_STOP:
16900 RequestQuitGame(TRUE);
16903 case GAME_CTRL_ID_PAUSE:
16904 if (options.network)
16906 #if defined(NETWORK_AVALIABLE)
16908 SendToServer_ContinuePlaying();
16910 SendToServer_PausePlaying();
16914 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16917 case GAME_CTRL_ID_PLAY:
16920 #if defined(NETWORK_AVALIABLE)
16921 if (options.network)
16922 SendToServer_ContinuePlaying();
16926 tape.pausing = FALSE;
16927 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16932 case SOUND_CTRL_ID_MUSIC:
16933 if (setup.sound_music)
16935 setup.sound_music = FALSE;
16939 else if (audio.music_available)
16941 setup.sound = setup.sound_music = TRUE;
16943 SetAudioMode(setup.sound);
16949 case SOUND_CTRL_ID_LOOPS:
16950 if (setup.sound_loops)
16951 setup.sound_loops = FALSE;
16952 else if (audio.loops_available)
16954 setup.sound = setup.sound_loops = TRUE;
16956 SetAudioMode(setup.sound);
16960 case SOUND_CTRL_ID_SIMPLE:
16961 if (setup.sound_simple)
16962 setup.sound_simple = FALSE;
16963 else if (audio.sound_available)
16965 setup.sound = setup.sound_simple = TRUE;
16967 SetAudioMode(setup.sound);
16976 static void HandleGameButtons(struct GadgetInfo *gi)
16978 HandleGameButtonsExt(gi->custom_id);
16981 void HandleSoundButtonKeys(Key key)
16984 if (key == setup.shortcut.sound_simple)
16985 ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16986 else if (key == setup.shortcut.sound_loops)
16987 ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16988 else if (key == setup.shortcut.sound_music)
16989 ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16991 if (key == setup.shortcut.sound_simple)
16992 HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16993 else if (key == setup.shortcut.sound_loops)
16994 HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16995 else if (key == setup.shortcut.sound_music)
16996 HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);