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_DrawLevelFieldCrumbledSand(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(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_DrawLevelFieldCrumbledSand(x, y) \
85 DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
87 DrawLevelFieldCrumbledSandNeighbours(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_SHIELD_NORMAL 35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
199 #define GAME_PANEL_SHIELD_DEADLY 37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
201 #define GAME_PANEL_EXIT 39
202 #define GAME_PANEL_EMC_MAGIC_BALL 40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
204 #define GAME_PANEL_LIGHT_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
206 #define GAME_PANEL_TIMEGATE_SWITCH 44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
208 #define GAME_PANEL_SWITCHGATE_SWITCH 46
209 #define GAME_PANEL_EMC_LENSES 47
210 #define GAME_PANEL_EMC_LENSES_TIME 48
211 #define GAME_PANEL_EMC_MAGNIFIER 49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
213 #define GAME_PANEL_BALLOON_SWITCH 51
214 #define GAME_PANEL_DYNABOMB_NUMBER 52
215 #define GAME_PANEL_DYNABOMB_SIZE 53
216 #define GAME_PANEL_DYNABOMB_POWER 54
217 #define GAME_PANEL_PENGUINS 55
218 #define GAME_PANEL_SOKOBAN_OBJECTS 56
219 #define GAME_PANEL_SOKOBAN_FIELDS 57
220 #define GAME_PANEL_ROBOT_WHEEL 58
221 #define GAME_PANEL_CONVEYOR_BELT_1 59
222 #define GAME_PANEL_CONVEYOR_BELT_2 60
223 #define GAME_PANEL_CONVEYOR_BELT_3 61
224 #define GAME_PANEL_CONVEYOR_BELT_4 62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
229 #define GAME_PANEL_MAGIC_WALL 67
230 #define GAME_PANEL_MAGIC_WALL_TIME 68
231 #define GAME_PANEL_GRAVITY_STATE 69
232 #define GAME_PANEL_GRAPHIC_1 70
233 #define GAME_PANEL_GRAPHIC_2 71
234 #define GAME_PANEL_GRAPHIC_3 72
235 #define GAME_PANEL_GRAPHIC_4 73
236 #define GAME_PANEL_GRAPHIC_5 74
237 #define GAME_PANEL_GRAPHIC_6 75
238 #define GAME_PANEL_GRAPHIC_7 76
239 #define GAME_PANEL_GRAPHIC_8 77
240 #define GAME_PANEL_ELEMENT_1 78
241 #define GAME_PANEL_ELEMENT_2 79
242 #define GAME_PANEL_ELEMENT_3 80
243 #define GAME_PANEL_ELEMENT_4 81
244 #define GAME_PANEL_ELEMENT_5 82
245 #define GAME_PANEL_ELEMENT_6 83
246 #define GAME_PANEL_ELEMENT_7 84
247 #define GAME_PANEL_ELEMENT_8 85
248 #define GAME_PANEL_ELEMENT_COUNT_1 86
249 #define GAME_PANEL_ELEMENT_COUNT_2 87
250 #define GAME_PANEL_ELEMENT_COUNT_3 88
251 #define GAME_PANEL_ELEMENT_COUNT_4 89
252 #define GAME_PANEL_ELEMENT_COUNT_5 90
253 #define GAME_PANEL_ELEMENT_COUNT_6 91
254 #define GAME_PANEL_ELEMENT_COUNT_7 92
255 #define GAME_PANEL_ELEMENT_COUNT_8 93
256 #define GAME_PANEL_CE_SCORE_1 94
257 #define GAME_PANEL_CE_SCORE_2 95
258 #define GAME_PANEL_CE_SCORE_3 96
259 #define GAME_PANEL_CE_SCORE_4 97
260 #define GAME_PANEL_CE_SCORE_5 98
261 #define GAME_PANEL_CE_SCORE_6 99
262 #define GAME_PANEL_CE_SCORE_7 100
263 #define GAME_PANEL_CE_SCORE_8 101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
272 #define GAME_PANEL_PLAYER_NAME 110
273 #define GAME_PANEL_LEVEL_NAME 111
274 #define GAME_PANEL_LEVEL_AUTHOR 112
276 #define NUM_GAME_PANEL_CONTROLS 113
278 struct GamePanelOrderInfo
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286 struct GamePanelControlInfo
290 struct TextPosInfo *pos;
293 int value, last_value;
294 int frame, last_frame;
299 static struct GamePanelControlInfo game_panel_controls[] =
302 GAME_PANEL_LEVEL_NUMBER,
303 &game.panel.level_number,
312 GAME_PANEL_INVENTORY_COUNT,
313 &game.panel.inventory_count,
317 GAME_PANEL_INVENTORY_FIRST_1,
318 &game.panel.inventory_first[0],
322 GAME_PANEL_INVENTORY_FIRST_2,
323 &game.panel.inventory_first[1],
327 GAME_PANEL_INVENTORY_FIRST_3,
328 &game.panel.inventory_first[2],
332 GAME_PANEL_INVENTORY_FIRST_4,
333 &game.panel.inventory_first[3],
337 GAME_PANEL_INVENTORY_FIRST_5,
338 &game.panel.inventory_first[4],
342 GAME_PANEL_INVENTORY_FIRST_6,
343 &game.panel.inventory_first[5],
347 GAME_PANEL_INVENTORY_FIRST_7,
348 &game.panel.inventory_first[6],
352 GAME_PANEL_INVENTORY_FIRST_8,
353 &game.panel.inventory_first[7],
357 GAME_PANEL_INVENTORY_LAST_1,
358 &game.panel.inventory_last[0],
362 GAME_PANEL_INVENTORY_LAST_2,
363 &game.panel.inventory_last[1],
367 GAME_PANEL_INVENTORY_LAST_3,
368 &game.panel.inventory_last[2],
372 GAME_PANEL_INVENTORY_LAST_4,
373 &game.panel.inventory_last[3],
377 GAME_PANEL_INVENTORY_LAST_5,
378 &game.panel.inventory_last[4],
382 GAME_PANEL_INVENTORY_LAST_6,
383 &game.panel.inventory_last[5],
387 GAME_PANEL_INVENTORY_LAST_7,
388 &game.panel.inventory_last[6],
392 GAME_PANEL_INVENTORY_LAST_8,
393 &game.panel.inventory_last[7],
437 GAME_PANEL_KEY_WHITE,
438 &game.panel.key_white,
442 GAME_PANEL_KEY_WHITE_COUNT,
443 &game.panel.key_white_count,
452 GAME_PANEL_HIGHSCORE,
453 &game.panel.highscore,
477 GAME_PANEL_SHIELD_NORMAL,
478 &game.panel.shield_normal,
482 GAME_PANEL_SHIELD_NORMAL_TIME,
483 &game.panel.shield_normal_time,
487 GAME_PANEL_SHIELD_DEADLY,
488 &game.panel.shield_deadly,
492 GAME_PANEL_SHIELD_DEADLY_TIME,
493 &game.panel.shield_deadly_time,
502 GAME_PANEL_EMC_MAGIC_BALL,
503 &game.panel.emc_magic_ball,
507 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508 &game.panel.emc_magic_ball_switch,
512 GAME_PANEL_LIGHT_SWITCH,
513 &game.panel.light_switch,
517 GAME_PANEL_LIGHT_SWITCH_TIME,
518 &game.panel.light_switch_time,
522 GAME_PANEL_TIMEGATE_SWITCH,
523 &game.panel.timegate_switch,
527 GAME_PANEL_TIMEGATE_SWITCH_TIME,
528 &game.panel.timegate_switch_time,
532 GAME_PANEL_SWITCHGATE_SWITCH,
533 &game.panel.switchgate_switch,
537 GAME_PANEL_EMC_LENSES,
538 &game.panel.emc_lenses,
542 GAME_PANEL_EMC_LENSES_TIME,
543 &game.panel.emc_lenses_time,
547 GAME_PANEL_EMC_MAGNIFIER,
548 &game.panel.emc_magnifier,
552 GAME_PANEL_EMC_MAGNIFIER_TIME,
553 &game.panel.emc_magnifier_time,
557 GAME_PANEL_BALLOON_SWITCH,
558 &game.panel.balloon_switch,
562 GAME_PANEL_DYNABOMB_NUMBER,
563 &game.panel.dynabomb_number,
567 GAME_PANEL_DYNABOMB_SIZE,
568 &game.panel.dynabomb_size,
572 GAME_PANEL_DYNABOMB_POWER,
573 &game.panel.dynabomb_power,
578 &game.panel.penguins,
582 GAME_PANEL_SOKOBAN_OBJECTS,
583 &game.panel.sokoban_objects,
587 GAME_PANEL_SOKOBAN_FIELDS,
588 &game.panel.sokoban_fields,
592 GAME_PANEL_ROBOT_WHEEL,
593 &game.panel.robot_wheel,
597 GAME_PANEL_CONVEYOR_BELT_1,
598 &game.panel.conveyor_belt[0],
602 GAME_PANEL_CONVEYOR_BELT_2,
603 &game.panel.conveyor_belt[1],
607 GAME_PANEL_CONVEYOR_BELT_3,
608 &game.panel.conveyor_belt[2],
612 GAME_PANEL_CONVEYOR_BELT_4,
613 &game.panel.conveyor_belt[3],
617 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618 &game.panel.conveyor_belt_switch[0],
622 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623 &game.panel.conveyor_belt_switch[1],
627 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628 &game.panel.conveyor_belt_switch[2],
632 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633 &game.panel.conveyor_belt_switch[3],
637 GAME_PANEL_MAGIC_WALL,
638 &game.panel.magic_wall,
642 GAME_PANEL_MAGIC_WALL_TIME,
643 &game.panel.magic_wall_time,
647 GAME_PANEL_GRAVITY_STATE,
648 &game.panel.gravity_state,
652 GAME_PANEL_GRAPHIC_1,
653 &game.panel.graphic[0],
657 GAME_PANEL_GRAPHIC_2,
658 &game.panel.graphic[1],
662 GAME_PANEL_GRAPHIC_3,
663 &game.panel.graphic[2],
667 GAME_PANEL_GRAPHIC_4,
668 &game.panel.graphic[3],
672 GAME_PANEL_GRAPHIC_5,
673 &game.panel.graphic[4],
677 GAME_PANEL_GRAPHIC_6,
678 &game.panel.graphic[5],
682 GAME_PANEL_GRAPHIC_7,
683 &game.panel.graphic[6],
687 GAME_PANEL_GRAPHIC_8,
688 &game.panel.graphic[7],
692 GAME_PANEL_ELEMENT_1,
693 &game.panel.element[0],
697 GAME_PANEL_ELEMENT_2,
698 &game.panel.element[1],
702 GAME_PANEL_ELEMENT_3,
703 &game.panel.element[2],
707 GAME_PANEL_ELEMENT_4,
708 &game.panel.element[3],
712 GAME_PANEL_ELEMENT_5,
713 &game.panel.element[4],
717 GAME_PANEL_ELEMENT_6,
718 &game.panel.element[5],
722 GAME_PANEL_ELEMENT_7,
723 &game.panel.element[6],
727 GAME_PANEL_ELEMENT_8,
728 &game.panel.element[7],
732 GAME_PANEL_ELEMENT_COUNT_1,
733 &game.panel.element_count[0],
737 GAME_PANEL_ELEMENT_COUNT_2,
738 &game.panel.element_count[1],
742 GAME_PANEL_ELEMENT_COUNT_3,
743 &game.panel.element_count[2],
747 GAME_PANEL_ELEMENT_COUNT_4,
748 &game.panel.element_count[3],
752 GAME_PANEL_ELEMENT_COUNT_5,
753 &game.panel.element_count[4],
757 GAME_PANEL_ELEMENT_COUNT_6,
758 &game.panel.element_count[5],
762 GAME_PANEL_ELEMENT_COUNT_7,
763 &game.panel.element_count[6],
767 GAME_PANEL_ELEMENT_COUNT_8,
768 &game.panel.element_count[7],
772 GAME_PANEL_CE_SCORE_1,
773 &game.panel.ce_score[0],
777 GAME_PANEL_CE_SCORE_2,
778 &game.panel.ce_score[1],
782 GAME_PANEL_CE_SCORE_3,
783 &game.panel.ce_score[2],
787 GAME_PANEL_CE_SCORE_4,
788 &game.panel.ce_score[3],
792 GAME_PANEL_CE_SCORE_5,
793 &game.panel.ce_score[4],
797 GAME_PANEL_CE_SCORE_6,
798 &game.panel.ce_score[5],
802 GAME_PANEL_CE_SCORE_7,
803 &game.panel.ce_score[6],
807 GAME_PANEL_CE_SCORE_8,
808 &game.panel.ce_score[7],
812 GAME_PANEL_CE_SCORE_1_ELEMENT,
813 &game.panel.ce_score_element[0],
817 GAME_PANEL_CE_SCORE_2_ELEMENT,
818 &game.panel.ce_score_element[1],
822 GAME_PANEL_CE_SCORE_3_ELEMENT,
823 &game.panel.ce_score_element[2],
827 GAME_PANEL_CE_SCORE_4_ELEMENT,
828 &game.panel.ce_score_element[3],
832 GAME_PANEL_CE_SCORE_5_ELEMENT,
833 &game.panel.ce_score_element[4],
837 GAME_PANEL_CE_SCORE_6_ELEMENT,
838 &game.panel.ce_score_element[5],
842 GAME_PANEL_CE_SCORE_7_ELEMENT,
843 &game.panel.ce_score_element[6],
847 GAME_PANEL_CE_SCORE_8_ELEMENT,
848 &game.panel.ce_score_element[7],
852 GAME_PANEL_PLAYER_NAME,
853 &game.panel.player_name,
857 GAME_PANEL_LEVEL_NAME,
858 &game.panel.level_name,
862 GAME_PANEL_LEVEL_AUTHOR,
863 &game.panel.level_author,
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING 3
878 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION 2
880 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF -1
884 #define INITIAL_MOVE_DELAY_ON 0
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED 32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED 4
890 #define MOVE_DELAY_MAX_SPEED 1
892 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
895 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN (1)
901 #define MOVE_STEPSIZE_MAX (TILEX)
903 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
906 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
908 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
909 RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
911 RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
913 RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
915 (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
917 RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
920 RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
922 RND((c)->delay_random))
925 #define GET_VALID_RUNTIME_ELEMENT(e) \
926 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
928 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
929 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
930 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
931 (be) + (e) - EL_SELF)
933 #define GET_PLAYER_FROM_BITS(p) \
934 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
937 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
938 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
939 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
940 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
941 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
942 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
943 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
944 RESOLVED_REFERENCE_ELEMENT(be, e) : \
947 #define CAN_GROW_INTO(e) \
948 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
951 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
955 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
956 (CAN_MOVE_INTO_ACID(e) && \
957 Feld[x][y] == EL_ACID) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
969 (CAN_MOVE_INTO_ACID(e) && \
970 Feld[x][y] == EL_ACID) || \
971 (DONT_COLLIDE_WITH(e) && \
973 !PLAYER_ENEMY_PROTECTED(x, y))))
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
978 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
984 #define ANDROID_CAN_CLONE_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
989 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1000 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005 Feld[x][y] == EL_EM_EXIT_OPEN || \
1006 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008 IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1013 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1019 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1020 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1022 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1024 #define CE_ENTER_FIELD_COND(e, x, y) \
1025 (!IS_PLAYER(x, y) && \
1026 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1029 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1034 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP 0
1041 #define GAME_CTRL_ID_PAUSE 1
1042 #define GAME_CTRL_ID_PLAY 2
1043 #define SOUND_CTRL_ID_MUSIC 3
1044 #define SOUND_CTRL_ID_LOOPS 4
1045 #define SOUND_CTRL_ID_SIMPLE 5
1047 #define NUM_GAME_BUTTONS 6
1050 /* forward declaration for internal use */
1052 static void CreateField(int, int, int);
1054 static void ResetGfxAnimation(int, int);
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev) \
1089 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1091 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1093 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev) \
1099 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1101 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1103 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1149 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1151 if (recursion_loop_detected) \
1154 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1156 recursion_loop_detected = TRUE; \
1157 recursion_loop_element = (e); \
1160 recursion_loop_depth++; \
1163 #define RECURSION_LOOP_DETECTION_END() \
1165 recursion_loop_depth--; \
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1172 static int map_player_action[MAX_PLAYERS];
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after */
1177 /* a specified time, eventually calling a function when changing */
1178 /* ------------------------------------------------------------------------- */
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1198 struct ChangingElementInfo
1203 void (*pre_change_function)(int x, int y);
1204 void (*change_function)(int x, int y);
1205 void (*post_change_function)(int x, int y);
1208 static struct ChangingElementInfo change_delay_list[] =
1243 EL_STEEL_EXIT_OPENING,
1251 EL_STEEL_EXIT_CLOSING,
1252 EL_STEEL_EXIT_CLOSED,
1279 EL_EM_STEEL_EXIT_OPENING,
1280 EL_EM_STEEL_EXIT_OPEN,
1287 EL_EM_STEEL_EXIT_CLOSING,
1291 EL_EM_STEEL_EXIT_CLOSED,
1315 EL_SWITCHGATE_OPENING,
1323 EL_SWITCHGATE_CLOSING,
1324 EL_SWITCHGATE_CLOSED,
1331 EL_TIMEGATE_OPENING,
1339 EL_TIMEGATE_CLOSING,
1348 EL_ACID_SPLASH_LEFT,
1356 EL_ACID_SPLASH_RIGHT,
1365 EL_SP_BUGGY_BASE_ACTIVATING,
1372 EL_SP_BUGGY_BASE_ACTIVATING,
1373 EL_SP_BUGGY_BASE_ACTIVE,
1380 EL_SP_BUGGY_BASE_ACTIVE,
1404 EL_ROBOT_WHEEL_ACTIVE,
1412 EL_TIMEGATE_SWITCH_ACTIVE,
1420 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421 EL_DC_TIMEGATE_SWITCH,
1428 EL_EMC_MAGIC_BALL_ACTIVE,
1429 EL_EMC_MAGIC_BALL_ACTIVE,
1436 EL_EMC_SPRING_BUMPER_ACTIVE,
1437 EL_EMC_SPRING_BUMPER,
1444 EL_DIAGONAL_SHRINKING,
1452 EL_DIAGONAL_GROWING,
1473 int push_delay_fixed, push_delay_random;
1477 { EL_SPRING, 0, 0 },
1478 { EL_BALLOON, 0, 0 },
1480 { EL_SOKOBAN_OBJECT, 2, 0 },
1481 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1482 { EL_SATELLITE, 2, 0 },
1483 { EL_SP_DISK_YELLOW, 2, 0 },
1485 { EL_UNDEFINED, 0, 0 },
1493 move_stepsize_list[] =
1495 { EL_AMOEBA_DROP, 2 },
1496 { EL_AMOEBA_DROPPING, 2 },
1497 { EL_QUICKSAND_FILLING, 1 },
1498 { EL_QUICKSAND_EMPTYING, 1 },
1499 { EL_QUICKSAND_FAST_FILLING, 2 },
1500 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501 { EL_MAGIC_WALL_FILLING, 2 },
1502 { EL_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_BD_MAGIC_WALL_FILLING, 2 },
1504 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1505 { EL_DC_MAGIC_WALL_FILLING, 2 },
1506 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1508 { EL_UNDEFINED, 0 },
1516 collect_count_list[] =
1519 { EL_BD_DIAMOND, 1 },
1520 { EL_EMERALD_YELLOW, 1 },
1521 { EL_EMERALD_RED, 1 },
1522 { EL_EMERALD_PURPLE, 1 },
1524 { EL_SP_INFOTRON, 1 },
1528 { EL_UNDEFINED, 0 },
1536 access_direction_list[] =
1538 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1540 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1541 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1542 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1543 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1544 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1545 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1546 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1547 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1548 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1550 { EL_SP_PORT_LEFT, MV_RIGHT },
1551 { EL_SP_PORT_RIGHT, MV_LEFT },
1552 { EL_SP_PORT_UP, MV_DOWN },
1553 { EL_SP_PORT_DOWN, MV_UP },
1554 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1555 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1556 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1561 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1562 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1563 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1564 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1565 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1566 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1567 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1568 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1570 { EL_UNDEFINED, MV_NONE }
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1575 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1578 IS_JUST_CHANGING(x, y))
1580 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1588 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1589 (y) >= 0 && (y) <= lev_fieldy - 1; \
1590 (y) += playfield_scan_delta_y) \
1591 for ((x) = playfield_scan_start_x; \
1592 (x) >= 0 && (x) <= lev_fieldx - 1; \
1593 (x) += playfield_scan_delta_x)
1596 void DEBUG_SetMaximumDynamite()
1600 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602 local_player->inventory_element[local_player->inventory_size++] =
1607 static void InitPlayfieldScanModeVars()
1609 if (game.use_reverse_scan_direction)
1611 playfield_scan_start_x = lev_fieldx - 1;
1612 playfield_scan_start_y = lev_fieldy - 1;
1614 playfield_scan_delta_x = -1;
1615 playfield_scan_delta_y = -1;
1619 playfield_scan_start_x = 0;
1620 playfield_scan_start_y = 0;
1622 playfield_scan_delta_x = 1;
1623 playfield_scan_delta_y = 1;
1627 static void InitPlayfieldScanMode(int mode)
1629 game.use_reverse_scan_direction =
1630 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1632 InitPlayfieldScanModeVars();
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1638 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1640 /* make sure that stepsize value is always a power of 2 */
1641 move_stepsize = (1 << log_2(move_stepsize));
1643 return TILEX / move_stepsize;
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1649 int player_nr = player->index_nr;
1650 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1653 /* do no immediately change move delay -- the player might just be moving */
1654 player->move_delay_value_next = move_delay;
1656 /* information if player can move must be set separately */
1657 player->cannot_move = cannot_move;
1661 player->move_delay = game.initial_move_delay[player_nr];
1662 player->move_delay_value = game.initial_move_delay_value[player_nr];
1664 player->move_delay_value_next = -1;
1666 player->move_delay_reset_counter = 0;
1670 void GetPlayerConfig()
1672 GameFrameDelay = setup.game_frame_delay;
1674 if (!audio.sound_available)
1675 setup.sound_simple = FALSE;
1677 if (!audio.loops_available)
1678 setup.sound_loops = FALSE;
1680 if (!audio.music_available)
1681 setup.sound_music = FALSE;
1683 if (!video.fullscreen_available)
1684 setup.fullscreen = FALSE;
1686 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1688 SetAudioMode(setup.sound);
1692 int GetElementFromGroupElement(int element)
1694 if (IS_GROUP_ELEMENT(element))
1696 struct ElementGroupInfo *group = element_info[element].group;
1697 int last_anim_random_frame = gfx.anim_random_frame;
1700 if (group->choice_mode == ANIM_RANDOM)
1701 gfx.anim_random_frame = RND(group->num_elements_resolved);
1703 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704 group->choice_mode, 0,
1707 if (group->choice_mode == ANIM_RANDOM)
1708 gfx.anim_random_frame = last_anim_random_frame;
1710 group->choice_pos++;
1712 element = group->element_resolved[element_pos];
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1720 if (element == EL_SP_MURPHY)
1724 if (stored_player[0].present)
1726 Feld[x][y] = EL_SP_MURPHY_CLONE;
1732 stored_player[0].initial_element = element;
1733 stored_player[0].use_murphy = TRUE;
1735 if (!level.use_artwork_element[0])
1736 stored_player[0].artwork_element = EL_SP_MURPHY;
1739 Feld[x][y] = EL_PLAYER_1;
1745 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746 int jx = player->jx, jy = player->jy;
1748 player->present = TRUE;
1750 player->block_last_field = (element == EL_SP_MURPHY ?
1751 level.sp_block_last_field :
1752 level.block_last_field);
1754 /* ---------- initialize player's last field block delay --------------- */
1756 /* always start with reliable default value (no adjustment needed) */
1757 player->block_delay_adjustment = 0;
1759 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760 if (player->block_last_field && element == EL_SP_MURPHY)
1761 player->block_delay_adjustment = 1;
1763 /* special case 2: in game engines before 3.1.1, blocking was different */
1764 if (game.use_block_last_field_bug)
1765 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1767 if (!options.network || player->connected)
1769 player->active = TRUE;
1771 /* remove potentially duplicate players */
1772 if (StorePlayer[jx][jy] == Feld[x][y])
1773 StorePlayer[jx][jy] = 0;
1775 StorePlayer[x][y] = Feld[x][y];
1779 printf("Player %d activated.\n", player->element_nr);
1780 printf("[Local player is %d and currently %s.]\n",
1781 local_player->element_nr,
1782 local_player->active ? "active" : "not active");
1786 Feld[x][y] = EL_EMPTY;
1788 player->jx = player->last_jx = x;
1789 player->jy = player->last_jy = y;
1792 #if USE_PLAYER_REANIMATION
1795 int player_nr = GET_PLAYER_NR(element);
1796 struct PlayerInfo *player = &stored_player[player_nr];
1798 if (player->active && player->killed)
1799 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1804 static void InitField(int x, int y, boolean init_game)
1806 int element = Feld[x][y];
1815 InitPlayerField(x, y, element, init_game);
1818 case EL_SOKOBAN_FIELD_PLAYER:
1819 element = Feld[x][y] = EL_PLAYER_1;
1820 InitField(x, y, init_game);
1822 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823 InitField(x, y, init_game);
1826 case EL_SOKOBAN_FIELD_EMPTY:
1827 local_player->sokobanfields_still_needed++;
1831 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1849 case EL_SPACESHIP_RIGHT:
1850 case EL_SPACESHIP_UP:
1851 case EL_SPACESHIP_LEFT:
1852 case EL_SPACESHIP_DOWN:
1853 case EL_BD_BUTTERFLY:
1854 case EL_BD_BUTTERFLY_RIGHT:
1855 case EL_BD_BUTTERFLY_UP:
1856 case EL_BD_BUTTERFLY_LEFT:
1857 case EL_BD_BUTTERFLY_DOWN:
1859 case EL_BD_FIREFLY_RIGHT:
1860 case EL_BD_FIREFLY_UP:
1861 case EL_BD_FIREFLY_LEFT:
1862 case EL_BD_FIREFLY_DOWN:
1863 case EL_PACMAN_RIGHT:
1865 case EL_PACMAN_LEFT:
1866 case EL_PACMAN_DOWN:
1868 case EL_YAMYAM_LEFT:
1869 case EL_YAMYAM_RIGHT:
1871 case EL_YAMYAM_DOWN:
1872 case EL_DARK_YAMYAM:
1875 case EL_SP_SNIKSNAK:
1876 case EL_SP_ELECTRON:
1885 case EL_AMOEBA_FULL:
1890 case EL_AMOEBA_DROP:
1891 if (y == lev_fieldy - 1)
1893 Feld[x][y] = EL_AMOEBA_GROWING;
1894 Store[x][y] = EL_AMOEBA_WET;
1898 case EL_DYNAMITE_ACTIVE:
1899 case EL_SP_DISK_RED_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904 MovDelay[x][y] = 96;
1907 case EL_EM_DYNAMITE_ACTIVE:
1908 MovDelay[x][y] = 32;
1912 local_player->lights_still_needed++;
1916 local_player->friends_still_needed++;
1921 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1924 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1938 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1942 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1944 game.belt_dir[belt_nr] = belt_dir;
1945 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1947 else /* more than one switch -- set it like the first switch */
1949 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1957 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1960 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1962 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1966 case EL_LIGHT_SWITCH_ACTIVE:
1968 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1971 case EL_INVISIBLE_STEELWALL:
1972 case EL_INVISIBLE_WALL:
1973 case EL_INVISIBLE_SAND:
1974 if (game.light_time_left > 0 ||
1975 game.lenses_time_left > 0)
1976 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1979 case EL_EMC_MAGIC_BALL:
1980 if (game.ball_state)
1981 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1984 case EL_EMC_MAGIC_BALL_SWITCH:
1985 if (game.ball_state)
1986 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1989 case EL_TRIGGER_PLAYER:
1990 case EL_TRIGGER_ELEMENT:
1991 case EL_TRIGGER_CE_VALUE:
1992 case EL_TRIGGER_CE_SCORE:
1994 case EL_ANY_ELEMENT:
1995 case EL_CURRENT_CE_VALUE:
1996 case EL_CURRENT_CE_SCORE:
2013 /* reference elements should not be used on the playfield */
2014 Feld[x][y] = EL_EMPTY;
2018 if (IS_CUSTOM_ELEMENT(element))
2020 if (CAN_MOVE(element))
2023 #if USE_NEW_CUSTOM_VALUE
2024 if (!element_info[element].use_last_ce_value || init_game)
2025 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2028 else if (IS_GROUP_ELEMENT(element))
2030 Feld[x][y] = GetElementFromGroupElement(element);
2032 InitField(x, y, init_game);
2039 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2044 InitField(x, y, init_game);
2046 /* not needed to call InitMovDir() -- already done by InitField()! */
2047 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048 CAN_MOVE(Feld[x][y]))
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2054 int old_element = Feld[x][y];
2056 InitField(x, y, init_game);
2058 /* not needed to call InitMovDir() -- already done by InitField()! */
2059 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060 CAN_MOVE(old_element) &&
2061 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2064 /* this case is in fact a combination of not less than three bugs:
2065 first, it calls InitMovDir() for elements that can move, although this is
2066 already done by InitField(); then, it checks the element that was at this
2067 field _before_ the call to InitField() (which can change it); lastly, it
2068 was not called for "mole with direction" elements, which were treated as
2069 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075 static int get_key_element_from_nr(int key_nr)
2077 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079 EL_EM_KEY_1 : EL_KEY_1);
2081 return key_base_element + key_nr;
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2086 return (player->inventory_size > 0 ?
2087 player->inventory_element[player->inventory_size - 1] :
2088 player->inventory_infinite_element != EL_UNDEFINED ?
2089 player->inventory_infinite_element :
2090 player->dynabombs_left > 0 ?
2091 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2097 /* pos >= 0: get element from bottom of the stack;
2098 pos < 0: get element from top of the stack */
2102 int min_inventory_size = -pos;
2103 int inventory_pos = player->inventory_size - min_inventory_size;
2104 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2106 return (player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2108 player->inventory_infinite_element != EL_UNDEFINED ?
2109 player->inventory_infinite_element :
2110 player->dynabombs_left >= min_dynabombs_left ?
2111 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116 int min_dynabombs_left = pos + 1;
2117 int min_inventory_size = pos + 1 - player->dynabombs_left;
2118 int inventory_pos = pos - player->dynabombs_left;
2120 return (player->inventory_infinite_element != EL_UNDEFINED ?
2121 player->inventory_infinite_element :
2122 player->dynabombs_left >= min_dynabombs_left ?
2123 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124 player->inventory_size >= min_inventory_size ?
2125 player->inventory_element[inventory_pos] :
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2132 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2136 if (gpo1->sort_priority != gpo2->sort_priority)
2137 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2139 compare_result = gpo1->nr - gpo2->nr;
2141 return compare_result;
2144 void InitGameControlValues()
2148 for (i = 0; game_panel_controls[i].nr != -1; i++)
2150 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152 struct TextPosInfo *pos = gpc->pos;
2154 int type = gpc->type;
2158 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159 Error(ERR_EXIT, "this should not happen -- please debug");
2162 /* force update of game controls after initialization */
2163 gpc->value = gpc->last_value = -1;
2164 gpc->frame = gpc->last_frame = -1;
2165 gpc->gfx_frame = -1;
2167 /* determine panel value width for later calculation of alignment */
2168 if (type == TYPE_INTEGER || type == TYPE_STRING)
2170 pos->width = pos->size * getFontWidth(pos->font);
2171 pos->height = getFontHeight(pos->font);
2173 else if (type == TYPE_ELEMENT)
2175 pos->width = pos->size;
2176 pos->height = pos->size;
2179 /* fill structure for game panel draw order */
2181 gpo->sort_priority = pos->sort_priority;
2184 /* sort game panel controls according to sort_priority and control number */
2185 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2189 void UpdatePlayfieldElementCount()
2191 boolean use_element_count = FALSE;
2194 /* first check if it is needed at all to calculate playfield element count */
2195 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197 use_element_count = TRUE;
2199 if (!use_element_count)
2202 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 element_info[i].element_count = 0;
2205 SCAN_PLAYFIELD(x, y)
2207 element_info[Feld[x][y]].element_count++;
2210 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212 if (IS_IN_GROUP(j, i))
2213 element_info[EL_GROUP_START + i].element_count +=
2214 element_info[j].element_count;
2217 void UpdateGameControlValues()
2220 int time = (local_player->LevelSolved ?
2221 local_player->LevelSolved_CountingTime :
2222 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223 level.native_em_level->lev->time :
2224 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2225 level.native_sp_level->game_sp->time_played :
2226 level.time == 0 ? TimePlayed : TimeLeft);
2227 int score = (local_player->LevelSolved ?
2228 local_player->LevelSolved_CountingScore :
2229 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230 level.native_em_level->lev->score :
2231 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2232 level.native_sp_level->game_sp->score :
2233 local_player->score);
2234 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235 level.native_em_level->lev->required :
2236 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237 level.native_sp_level->game_sp->infotrons_still_needed :
2238 local_player->gems_still_needed);
2239 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2240 level.native_em_level->lev->required > 0 :
2241 level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2242 level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2243 local_player->gems_still_needed > 0 ||
2244 local_player->sokobanfields_still_needed > 0 ||
2245 local_player->lights_still_needed > 0);
2247 UpdatePlayfieldElementCount();
2249 /* update game panel control values */
2251 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2252 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2254 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2255 for (i = 0; i < MAX_NUM_KEYS; i++)
2256 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2257 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2258 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2260 if (game.centered_player_nr == -1)
2262 for (i = 0; i < MAX_PLAYERS; i++)
2264 /* only one player in Supaplex game engine */
2265 if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2268 for (k = 0; k < MAX_NUM_KEYS; k++)
2270 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2272 if (level.native_em_level->ply[i]->keys & (1 << k))
2273 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2274 get_key_element_from_nr(k);
2276 else if (stored_player[i].key[k])
2277 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2278 get_key_element_from_nr(k);
2281 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2282 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2283 level.native_em_level->ply[i]->dynamite;
2284 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2285 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2286 level.native_sp_level->game_sp->red_disk_count;
2288 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 stored_player[i].inventory_size;
2291 if (stored_player[i].num_white_keys > 0)
2292 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2295 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2296 stored_player[i].num_white_keys;
2301 int player_nr = game.centered_player_nr;
2303 for (k = 0; k < MAX_NUM_KEYS; k++)
2305 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2307 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2308 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2309 get_key_element_from_nr(k);
2311 else if (stored_player[player_nr].key[k])
2312 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313 get_key_element_from_nr(k);
2316 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2317 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2318 level.native_em_level->ply[player_nr]->dynamite;
2319 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2320 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321 level.native_sp_level->game_sp->red_disk_count;
2323 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 stored_player[player_nr].inventory_size;
2326 if (stored_player[player_nr].num_white_keys > 0)
2327 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2329 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2330 stored_player[player_nr].num_white_keys;
2333 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2335 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2336 get_inventory_element_from_pos(local_player, i);
2337 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2338 get_inventory_element_from_pos(local_player, -i - 1);
2341 game_panel_controls[GAME_PANEL_SCORE].value = score;
2342 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2344 game_panel_controls[GAME_PANEL_TIME].value = time;
2346 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2347 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2348 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2350 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2351 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2353 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2354 local_player->shield_normal_time_left;
2355 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2356 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2358 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2359 local_player->shield_deadly_time_left;
2361 game_panel_controls[GAME_PANEL_EXIT].value =
2362 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2364 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2365 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2366 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2367 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2368 EL_EMC_MAGIC_BALL_SWITCH);
2370 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2371 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2372 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2373 game.light_time_left;
2375 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2376 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2377 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2378 game.timegate_time_left;
2380 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2381 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2383 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2384 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2385 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2386 game.lenses_time_left;
2388 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2389 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2390 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2391 game.magnify_time_left;
2393 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2394 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2395 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2396 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2397 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2398 EL_BALLOON_SWITCH_NONE);
2400 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2401 local_player->dynabomb_count;
2402 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2403 local_player->dynabomb_size;
2404 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2405 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2407 game_panel_controls[GAME_PANEL_PENGUINS].value =
2408 local_player->friends_still_needed;
2410 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2411 local_player->sokobanfields_still_needed;
2412 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2413 local_player->sokobanfields_still_needed;
2415 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2416 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2418 for (i = 0; i < NUM_BELTS; i++)
2420 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2421 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2422 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2423 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2424 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2427 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2428 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2429 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2430 game.magic_wall_time_left;
2432 #if USE_PLAYER_GRAVITY
2433 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2434 local_player->gravity;
2436 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2439 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2440 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2442 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2443 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2444 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2445 game.panel.element[i].id : EL_UNDEFINED);
2447 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2448 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2449 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2450 element_info[game.panel.element_count[i].id].element_count : 0);
2452 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2453 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2454 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2455 element_info[game.panel.ce_score[i].id].collect_score : 0);
2457 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2458 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2459 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2460 element_info[game.panel.ce_score_element[i].id].collect_score :
2463 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2464 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2465 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2467 /* update game panel control frames */
2469 for (i = 0; game_panel_controls[i].nr != -1; i++)
2471 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2473 if (gpc->type == TYPE_ELEMENT)
2475 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2477 int last_anim_random_frame = gfx.anim_random_frame;
2478 int element = gpc->value;
2479 int graphic = el2panelimg(element);
2481 if (gpc->value != gpc->last_value)
2484 gpc->gfx_random = INIT_GFX_RANDOM();
2490 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2491 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2492 gpc->gfx_random = INIT_GFX_RANDOM();
2495 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2496 gfx.anim_random_frame = gpc->gfx_random;
2498 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2499 gpc->gfx_frame = element_info[element].collect_score;
2501 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2504 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2505 gfx.anim_random_frame = last_anim_random_frame;
2511 void DisplayGameControlValues()
2513 boolean redraw_panel = FALSE;
2516 for (i = 0; game_panel_controls[i].nr != -1; i++)
2518 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2520 if (PANEL_DEACTIVATED(gpc->pos))
2523 if (gpc->value == gpc->last_value &&
2524 gpc->frame == gpc->last_frame)
2527 redraw_panel = TRUE;
2533 /* copy default game door content to main double buffer */
2534 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2535 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2537 /* redraw game control buttons */
2539 RedrawGameButtons();
2545 game_status = GAME_MODE_PSEUDO_PANEL;
2548 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2550 for (i = 0; game_panel_controls[i].nr != -1; i++)
2554 int nr = game_panel_order[i].nr;
2555 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2557 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560 struct TextPosInfo *pos = gpc->pos;
2561 int type = gpc->type;
2562 int value = gpc->value;
2563 int frame = gpc->frame;
2565 int last_value = gpc->last_value;
2566 int last_frame = gpc->last_frame;
2568 int size = pos->size;
2569 int font = pos->font;
2570 boolean draw_masked = pos->draw_masked;
2571 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2573 if (PANEL_DEACTIVATED(pos))
2577 if (value == last_value && frame == last_frame)
2581 gpc->last_value = value;
2582 gpc->last_frame = frame;
2585 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2588 if (type == TYPE_INTEGER)
2590 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2591 nr == GAME_PANEL_TIME)
2593 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2595 if (use_dynamic_size) /* use dynamic number of digits */
2597 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2598 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2599 int size2 = size1 + 1;
2600 int font1 = pos->font;
2601 int font2 = pos->font_alt;
2603 size = (value < value_change ? size1 : size2);
2604 font = (value < value_change ? font1 : font2);
2607 /* clear background if value just changed its size (dynamic digits) */
2608 if ((last_value < value_change) != (value < value_change))
2610 int width1 = size1 * getFontWidth(font1);
2611 int width2 = size2 * getFontWidth(font2);
2612 int max_width = MAX(width1, width2);
2613 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2615 pos->width = max_width;
2617 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2618 max_width, max_height);
2625 /* correct text size if "digits" is zero or less */
2627 size = strlen(int2str(value, size));
2629 /* dynamically correct text alignment */
2630 pos->width = size * getFontWidth(font);
2633 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2634 int2str(value, size), font, mask_mode);
2636 else if (type == TYPE_ELEMENT)
2638 int element, graphic;
2642 int dst_x = PANEL_XPOS(pos);
2643 int dst_y = PANEL_YPOS(pos);
2646 if (value != EL_UNDEFINED && value != EL_EMPTY)
2649 graphic = el2panelimg(value);
2651 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2654 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2658 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2661 width = graphic_info[graphic].width * size / TILESIZE;
2662 height = graphic_info[graphic].height * size / TILESIZE;
2666 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2667 dst_x - src_x, dst_y - src_y);
2668 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2673 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2678 if (value == EL_UNDEFINED || value == EL_EMPTY)
2680 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2681 graphic = el2panelimg(element);
2683 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2684 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2685 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2690 graphic = el2panelimg(value);
2692 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2695 width = graphic_info[graphic].width * size / TILESIZE;
2696 height = graphic_info[graphic].height * size / TILESIZE;
2698 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2701 else if (type == TYPE_STRING)
2703 boolean active = (value != 0);
2704 char *state_normal = "off";
2705 char *state_active = "on";
2706 char *state = (active ? state_active : state_normal);
2707 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2708 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2709 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2710 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2712 if (nr == GAME_PANEL_GRAVITY_STATE)
2714 int font1 = pos->font; /* (used for normal state) */
2715 int font2 = pos->font_alt; /* (used for active state) */
2717 int size1 = strlen(state_normal);
2718 int size2 = strlen(state_active);
2719 int width1 = size1 * getFontWidth(font1);
2720 int width2 = size2 * getFontWidth(font2);
2721 int max_width = MAX(width1, width2);
2722 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2724 pos->width = max_width;
2726 /* clear background for values that may have changed its size */
2727 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2728 max_width, max_height);
2731 font = (active ? font2 : font1);
2741 /* don't truncate output if "chars" is zero or less */
2744 /* dynamically correct text alignment */
2745 pos->width = size * getFontWidth(font);
2749 s_cut = getStringCopyN(s, size);
2751 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2752 s_cut, font, mask_mode);
2758 redraw_mask |= REDRAW_DOOR_1;
2761 game_status = GAME_MODE_PLAYING;
2764 void UpdateAndDisplayGameControlValues()
2766 if (tape.warp_forward)
2769 UpdateGameControlValues();
2770 DisplayGameControlValues();
2773 void DrawGameValue_Emeralds(int value)
2775 struct TextPosInfo *pos = &game.panel.gems;
2777 int font_nr = pos->font;
2779 int font_nr = FONT_TEXT_2;
2781 int font_width = getFontWidth(font_nr);
2782 int chars = pos->size;
2785 return; /* !!! USE NEW STUFF !!! */
2788 if (PANEL_DEACTIVATED(pos))
2791 pos->width = chars * font_width;
2793 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2796 void DrawGameValue_Dynamite(int value)
2798 struct TextPosInfo *pos = &game.panel.inventory_count;
2800 int font_nr = pos->font;
2802 int font_nr = FONT_TEXT_2;
2804 int font_width = getFontWidth(font_nr);
2805 int chars = pos->size;
2808 return; /* !!! USE NEW STUFF !!! */
2811 if (PANEL_DEACTIVATED(pos))
2814 pos->width = chars * font_width;
2816 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2819 void DrawGameValue_Score(int value)
2821 struct TextPosInfo *pos = &game.panel.score;
2823 int font_nr = pos->font;
2825 int font_nr = FONT_TEXT_2;
2827 int font_width = getFontWidth(font_nr);
2828 int chars = pos->size;
2831 return; /* !!! USE NEW STUFF !!! */
2834 if (PANEL_DEACTIVATED(pos))
2837 pos->width = chars * font_width;
2839 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2842 void DrawGameValue_Time(int value)
2844 struct TextPosInfo *pos = &game.panel.time;
2845 static int last_value = -1;
2848 int chars = pos->size;
2850 int font1_nr = pos->font;
2851 int font2_nr = pos->font_alt;
2853 int font1_nr = FONT_TEXT_2;
2854 int font2_nr = FONT_TEXT_1;
2856 int font_nr = font1_nr;
2857 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2860 return; /* !!! USE NEW STUFF !!! */
2863 if (PANEL_DEACTIVATED(pos))
2866 if (use_dynamic_chars) /* use dynamic number of chars */
2868 chars = (value < 1000 ? chars1 : chars2);
2869 font_nr = (value < 1000 ? font1_nr : font2_nr);
2872 /* clear background if value just changed its size (dynamic chars only) */
2873 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2875 int width1 = chars1 * getFontWidth(font1_nr);
2876 int width2 = chars2 * getFontWidth(font2_nr);
2877 int max_width = MAX(width1, width2);
2878 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2880 pos->width = max_width;
2882 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2883 max_width, max_height);
2886 pos->width = chars * getFontWidth(font_nr);
2888 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2893 void DrawGameValue_Level(int value)
2895 struct TextPosInfo *pos = &game.panel.level_number;
2898 int chars = pos->size;
2900 int font1_nr = pos->font;
2901 int font2_nr = pos->font_alt;
2903 int font1_nr = FONT_TEXT_2;
2904 int font2_nr = FONT_TEXT_1;
2906 int font_nr = font1_nr;
2907 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2910 return; /* !!! USE NEW STUFF !!! */
2913 if (PANEL_DEACTIVATED(pos))
2916 if (use_dynamic_chars) /* use dynamic number of chars */
2918 chars = (level_nr < 100 ? chars1 : chars2);
2919 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2922 pos->width = chars * getFontWidth(font_nr);
2924 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2927 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2930 struct TextPosInfo *pos = &game.panel.keys;
2933 int base_key_graphic = EL_KEY_1;
2938 return; /* !!! USE NEW STUFF !!! */
2942 if (PANEL_DEACTIVATED(pos))
2947 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2948 base_key_graphic = EL_EM_KEY_1;
2952 pos->width = 4 * MINI_TILEX;
2956 for (i = 0; i < MAX_NUM_KEYS; i++)
2958 /* currently only 4 of 8 possible keys are displayed */
2959 for (i = 0; i < STD_NUM_KEYS; i++)
2963 struct TextPosInfo *pos = &game.panel.key[i];
2965 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2966 int src_y = DOOR_GFX_PAGEY1 + 123;
2968 int dst_x = PANEL_XPOS(pos);
2969 int dst_y = PANEL_YPOS(pos);
2971 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2972 int dst_y = PANEL_YPOS(pos);
2976 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2977 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2979 int graphic = el2edimg(element);
2983 if (PANEL_DEACTIVATED(pos))
2988 /* masked blit with tiles from half-size scaled bitmap does not work yet
2989 (no mask bitmap created for these sizes after loading and scaling) --
2990 solution: load without creating mask, scale, then create final mask */
2992 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2993 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2998 int graphic = el2edimg(base_key_graphic + i);
3003 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3005 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3006 dst_x - src_x, dst_y - src_y);
3007 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3013 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3015 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3016 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3019 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3021 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3022 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3030 void DrawGameValue_Emeralds(int value)
3032 int font_nr = FONT_TEXT_2;
3033 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3035 if (PANEL_DEACTIVATED(game.panel.gems))
3038 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3041 void DrawGameValue_Dynamite(int value)
3043 int font_nr = FONT_TEXT_2;
3044 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3046 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3049 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3052 void DrawGameValue_Score(int value)
3054 int font_nr = FONT_TEXT_2;
3055 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3057 if (PANEL_DEACTIVATED(game.panel.score))
3060 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3063 void DrawGameValue_Time(int value)
3065 int font1_nr = FONT_TEXT_2;
3067 int font2_nr = FONT_TEXT_1;
3069 int font2_nr = FONT_LEVEL_NUMBER;
3071 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3072 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3074 if (PANEL_DEACTIVATED(game.panel.time))
3077 /* clear background if value just changed its size */
3078 if (value == 999 || value == 1000)
3079 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3082 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3084 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3087 void DrawGameValue_Level(int value)
3089 int font1_nr = FONT_TEXT_2;
3091 int font2_nr = FONT_TEXT_1;
3093 int font2_nr = FONT_LEVEL_NUMBER;
3096 if (PANEL_DEACTIVATED(game.panel.level))
3100 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3102 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3105 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3107 int base_key_graphic = EL_KEY_1;
3110 if (PANEL_DEACTIVATED(game.panel.keys))
3113 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3114 base_key_graphic = EL_EM_KEY_1;
3116 /* currently only 4 of 8 possible keys are displayed */
3117 for (i = 0; i < STD_NUM_KEYS; i++)
3119 int x = XX_KEYS + i * MINI_TILEX;
3123 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3125 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3126 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3132 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3135 int key[MAX_NUM_KEYS];
3138 /* prevent EM engine from updating time/score values parallel to GameWon() */
3139 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3140 local_player->LevelSolved)
3143 for (i = 0; i < MAX_NUM_KEYS; i++)
3144 key[i] = key_bits & (1 << i);
3146 DrawGameValue_Level(level_nr);
3148 DrawGameValue_Emeralds(emeralds);
3149 DrawGameValue_Dynamite(dynamite);
3150 DrawGameValue_Score(score);
3151 DrawGameValue_Time(time);
3153 DrawGameValue_Keys(key);
3156 void UpdateGameDoorValues()
3158 UpdateGameControlValues();
3161 void DrawGameDoorValues()
3163 DisplayGameControlValues();
3166 void DrawGameDoorValues_OLD()
3168 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3169 int dynamite_value = 0;
3170 int score_value = (local_player->LevelSolved ? local_player->score_final :
3171 local_player->score);
3172 int gems_value = local_player->gems_still_needed;
3176 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3178 DrawGameDoorValues_EM();
3183 if (game.centered_player_nr == -1)
3185 for (i = 0; i < MAX_PLAYERS; i++)
3187 for (j = 0; j < MAX_NUM_KEYS; j++)
3188 if (stored_player[i].key[j])
3189 key_bits |= (1 << j);
3191 dynamite_value += stored_player[i].inventory_size;
3196 int player_nr = game.centered_player_nr;
3198 for (i = 0; i < MAX_NUM_KEYS; i++)
3199 if (stored_player[player_nr].key[i])
3200 key_bits |= (1 << i);
3202 dynamite_value = stored_player[player_nr].inventory_size;
3205 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3211 =============================================================================
3213 -----------------------------------------------------------------------------
3214 initialize game engine due to level / tape version number
3215 =============================================================================
3218 static void InitGameEngine()
3220 int i, j, k, l, x, y;
3222 /* set game engine from tape file when re-playing, else from level file */
3223 game.engine_version = (tape.playing ? tape.engine_version :
3224 level.game_version);
3226 /* ---------------------------------------------------------------------- */
3227 /* set flags for bugs and changes according to active game engine version */
3228 /* ---------------------------------------------------------------------- */
3231 Summary of bugfix/change:
3232 Fixed handling for custom elements that change when pushed by the player.
3234 Fixed/changed in version:
3238 Before 3.1.0, custom elements that "change when pushing" changed directly
3239 after the player started pushing them (until then handled in "DigField()").
3240 Since 3.1.0, these custom elements are not changed until the "pushing"
3241 move of the element is finished (now handled in "ContinueMoving()").
3243 Affected levels/tapes:
3244 The first condition is generally needed for all levels/tapes before version
3245 3.1.0, which might use the old behaviour before it was changed; known tapes
3246 that are affected are some tapes from the level set "Walpurgis Gardens" by
3248 The second condition is an exception from the above case and is needed for
3249 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3250 above (including some development versions of 3.1.0), but before it was
3251 known that this change would break tapes like the above and was fixed in
3252 3.1.1, so that the changed behaviour was active although the engine version
3253 while recording maybe was before 3.1.0. There is at least one tape that is
3254 affected by this exception, which is the tape for the one-level set "Bug
3255 Machine" by Juergen Bonhagen.
3258 game.use_change_when_pushing_bug =
3259 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3261 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3262 tape.game_version < VERSION_IDENT(3,1,1,0)));
3265 Summary of bugfix/change:
3266 Fixed handling for blocking the field the player leaves when moving.
3268 Fixed/changed in version:
3272 Before 3.1.1, when "block last field when moving" was enabled, the field
3273 the player is leaving when moving was blocked for the time of the move,
3274 and was directly unblocked afterwards. This resulted in the last field
3275 being blocked for exactly one less than the number of frames of one player
3276 move. Additionally, even when blocking was disabled, the last field was
3277 blocked for exactly one frame.
3278 Since 3.1.1, due to changes in player movement handling, the last field
3279 is not blocked at all when blocking is disabled. When blocking is enabled,
3280 the last field is blocked for exactly the number of frames of one player
3281 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3282 last field is blocked for exactly one more than the number of frames of
3285 Affected levels/tapes:
3286 (!!! yet to be determined -- probably many !!!)
3289 game.use_block_last_field_bug =
3290 (game.engine_version < VERSION_IDENT(3,1,1,0));
3293 Summary of bugfix/change:
3294 Changed behaviour of CE changes with multiple changes per single frame.
3296 Fixed/changed in version:
3300 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3301 This resulted in race conditions where CEs seem to behave strange in some
3302 situations (where triggered CE changes were just skipped because there was
3303 already a CE change on that tile in the playfield in that engine frame).
3304 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3305 (The number of changes per frame must be limited in any case, because else
3306 it is easily possible to define CE changes that would result in an infinite
3307 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3308 should be set large enough so that it would only be reached in cases where
3309 the corresponding CE change conditions run into a loop. Therefore, it seems
3310 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3311 maximal number of change pages for custom elements.)
3313 Affected levels/tapes:
3317 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3318 game.max_num_changes_per_frame = 1;
3320 game.max_num_changes_per_frame =
3321 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3324 /* ---------------------------------------------------------------------- */
3326 /* default scan direction: scan playfield from top/left to bottom/right */
3327 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3329 /* dynamically adjust element properties according to game engine version */
3330 InitElementPropertiesEngine(game.engine_version);
3333 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3334 printf(" tape version == %06d [%s] [file: %06d]\n",
3335 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3337 printf(" => game.engine_version == %06d\n", game.engine_version);
3340 /* ---------- initialize player's initial move delay --------------------- */
3342 /* dynamically adjust player properties according to level information */
3343 for (i = 0; i < MAX_PLAYERS; i++)
3344 game.initial_move_delay_value[i] =
3345 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3347 /* dynamically adjust player properties according to game engine version */
3348 for (i = 0; i < MAX_PLAYERS; i++)
3349 game.initial_move_delay[i] =
3350 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3351 game.initial_move_delay_value[i] : 0);
3353 /* ---------- initialize player's initial push delay --------------------- */
3355 /* dynamically adjust player properties according to game engine version */
3356 game.initial_push_delay_value =
3357 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3359 /* ---------- initialize changing elements ------------------------------- */
3361 /* initialize changing elements information */
3362 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3364 struct ElementInfo *ei = &element_info[i];
3366 /* this pointer might have been changed in the level editor */
3367 ei->change = &ei->change_page[0];
3369 if (!IS_CUSTOM_ELEMENT(i))
3371 ei->change->target_element = EL_EMPTY_SPACE;
3372 ei->change->delay_fixed = 0;
3373 ei->change->delay_random = 0;
3374 ei->change->delay_frames = 1;
3377 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3379 ei->has_change_event[j] = FALSE;
3381 ei->event_page_nr[j] = 0;
3382 ei->event_page[j] = &ei->change_page[0];
3386 /* add changing elements from pre-defined list */
3387 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3389 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3390 struct ElementInfo *ei = &element_info[ch_delay->element];
3392 ei->change->target_element = ch_delay->target_element;
3393 ei->change->delay_fixed = ch_delay->change_delay;
3395 ei->change->pre_change_function = ch_delay->pre_change_function;
3396 ei->change->change_function = ch_delay->change_function;
3397 ei->change->post_change_function = ch_delay->post_change_function;
3399 ei->change->can_change = TRUE;
3400 ei->change->can_change_or_has_action = TRUE;
3402 ei->has_change_event[CE_DELAY] = TRUE;
3404 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3405 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3408 /* ---------- initialize internal run-time variables --------------------- */
3410 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3412 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3414 for (j = 0; j < ei->num_change_pages; j++)
3416 ei->change_page[j].can_change_or_has_action =
3417 (ei->change_page[j].can_change |
3418 ei->change_page[j].has_action);
3422 /* add change events from custom element configuration */
3423 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3425 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3427 for (j = 0; j < ei->num_change_pages; j++)
3429 if (!ei->change_page[j].can_change_or_has_action)
3432 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3434 /* only add event page for the first page found with this event */
3435 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3437 ei->has_change_event[k] = TRUE;
3439 ei->event_page_nr[k] = j;
3440 ei->event_page[k] = &ei->change_page[j];
3447 /* ---------- initialize reference elements in change conditions --------- */
3449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3451 int element = EL_CUSTOM_START + i;
3452 struct ElementInfo *ei = &element_info[element];
3454 for (j = 0; j < ei->num_change_pages; j++)
3456 int trigger_element = ei->change_page[j].initial_trigger_element;
3458 if (trigger_element >= EL_PREV_CE_8 &&
3459 trigger_element <= EL_NEXT_CE_8)
3460 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3462 ei->change_page[j].trigger_element = trigger_element;
3467 /* ---------- initialize run-time trigger player and element ------------- */
3469 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3471 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3473 for (j = 0; j < ei->num_change_pages; j++)
3475 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3476 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3477 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3478 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3479 ei->change_page[j].actual_trigger_ce_value = 0;
3480 ei->change_page[j].actual_trigger_ce_score = 0;
3484 /* ---------- initialize trigger events ---------------------------------- */
3486 /* initialize trigger events information */
3487 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3488 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3489 trigger_events[i][j] = FALSE;
3491 /* add trigger events from element change event properties */
3492 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3494 struct ElementInfo *ei = &element_info[i];
3496 for (j = 0; j < ei->num_change_pages; j++)
3498 if (!ei->change_page[j].can_change_or_has_action)
3501 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3503 int trigger_element = ei->change_page[j].trigger_element;
3505 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3507 if (ei->change_page[j].has_event[k])
3509 if (IS_GROUP_ELEMENT(trigger_element))
3511 struct ElementGroupInfo *group =
3512 element_info[trigger_element].group;
3514 for (l = 0; l < group->num_elements_resolved; l++)
3515 trigger_events[group->element_resolved[l]][k] = TRUE;
3517 else if (trigger_element == EL_ANY_ELEMENT)
3518 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3519 trigger_events[l][k] = TRUE;
3521 trigger_events[trigger_element][k] = TRUE;
3528 /* ---------- initialize push delay -------------------------------------- */
3530 /* initialize push delay values to default */
3531 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3533 if (!IS_CUSTOM_ELEMENT(i))
3535 /* set default push delay values (corrected since version 3.0.7-1) */
3536 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3538 element_info[i].push_delay_fixed = 2;
3539 element_info[i].push_delay_random = 8;
3543 element_info[i].push_delay_fixed = 8;
3544 element_info[i].push_delay_random = 8;
3549 /* set push delay value for certain elements from pre-defined list */
3550 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3552 int e = push_delay_list[i].element;
3554 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3555 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3558 /* set push delay value for Supaplex elements for newer engine versions */
3559 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3561 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3563 if (IS_SP_ELEMENT(i))
3565 /* set SP push delay to just enough to push under a falling zonk */
3566 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3568 element_info[i].push_delay_fixed = delay;
3569 element_info[i].push_delay_random = 0;
3574 /* ---------- initialize move stepsize ----------------------------------- */
3576 /* initialize move stepsize values to default */
3577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578 if (!IS_CUSTOM_ELEMENT(i))
3579 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3581 /* set move stepsize value for certain elements from pre-defined list */
3582 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3584 int e = move_stepsize_list[i].element;
3586 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3589 /* ---------- initialize collect score ----------------------------------- */
3591 /* initialize collect score values for custom elements from initial value */
3592 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3593 if (IS_CUSTOM_ELEMENT(i))
3594 element_info[i].collect_score = element_info[i].collect_score_initial;
3596 /* ---------- initialize collect count ----------------------------------- */
3598 /* initialize collect count values for non-custom elements */
3599 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3600 if (!IS_CUSTOM_ELEMENT(i))
3601 element_info[i].collect_count_initial = 0;
3603 /* add collect count values for all elements from pre-defined list */
3604 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3605 element_info[collect_count_list[i].element].collect_count_initial =
3606 collect_count_list[i].count;
3608 /* ---------- initialize access direction -------------------------------- */
3610 /* initialize access direction values to default (access from every side) */
3611 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3612 if (!IS_CUSTOM_ELEMENT(i))
3613 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3615 /* set access direction value for certain elements from pre-defined list */
3616 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3617 element_info[access_direction_list[i].element].access_direction =
3618 access_direction_list[i].direction;
3620 /* ---------- initialize explosion content ------------------------------- */
3621 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3623 if (IS_CUSTOM_ELEMENT(i))
3626 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3628 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3630 element_info[i].content.e[x][y] =
3631 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3632 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3633 i == EL_PLAYER_3 ? EL_EMERALD :
3634 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3635 i == EL_MOLE ? EL_EMERALD_RED :
3636 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3637 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3638 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3639 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3640 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3641 i == EL_WALL_EMERALD ? EL_EMERALD :
3642 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3643 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3644 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3645 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3646 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3647 i == EL_WALL_PEARL ? EL_PEARL :
3648 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3653 /* ---------- initialize recursion detection ------------------------------ */
3654 recursion_loop_depth = 0;
3655 recursion_loop_detected = FALSE;
3656 recursion_loop_element = EL_UNDEFINED;
3658 /* ---------- initialize graphics engine ---------------------------------- */
3659 game.scroll_delay_value =
3660 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3661 setup.scroll_delay ? setup.scroll_delay_value : 0);
3662 game.scroll_delay_value =
3663 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3666 int get_num_special_action(int element, int action_first, int action_last)
3668 int num_special_action = 0;
3671 for (i = action_first; i <= action_last; i++)
3673 boolean found = FALSE;
3675 for (j = 0; j < NUM_DIRECTIONS; j++)
3676 if (el_act_dir2img(element, i, j) !=
3677 el_act_dir2img(element, ACTION_DEFAULT, j))
3681 num_special_action++;
3686 return num_special_action;
3691 =============================================================================
3693 -----------------------------------------------------------------------------
3694 initialize and start new game
3695 =============================================================================
3700 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3701 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3702 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3704 boolean do_fading = (game_status == GAME_MODE_MAIN);
3707 int initial_move_dir = MV_DOWN;
3709 int initial_move_dir = MV_NONE;
3713 game_status = GAME_MODE_PLAYING;
3716 InitGameControlValues();
3718 /* don't play tapes over network */
3719 network_playing = (options.network && !tape.playing);
3721 for (i = 0; i < MAX_PLAYERS; i++)
3723 struct PlayerInfo *player = &stored_player[i];
3725 player->index_nr = i;
3726 player->index_bit = (1 << i);
3727 player->element_nr = EL_PLAYER_1 + i;
3729 player->present = FALSE;
3730 player->active = FALSE;
3731 player->mapped = FALSE;
3733 player->killed = FALSE;
3734 player->reanimated = FALSE;
3737 player->effective_action = 0;
3738 player->programmed_action = 0;
3741 player->score_final = 0;
3743 player->gems_still_needed = level.gems_needed;
3744 player->sokobanfields_still_needed = 0;
3745 player->lights_still_needed = 0;
3746 player->friends_still_needed = 0;
3748 for (j = 0; j < MAX_NUM_KEYS; j++)
3749 player->key[j] = FALSE;
3751 player->num_white_keys = 0;
3753 player->dynabomb_count = 0;
3754 player->dynabomb_size = 1;
3755 player->dynabombs_left = 0;
3756 player->dynabomb_xl = FALSE;
3758 player->MovDir = initial_move_dir;
3761 player->GfxDir = initial_move_dir;
3762 player->GfxAction = ACTION_DEFAULT;
3764 player->StepFrame = 0;
3766 player->initial_element = player->element_nr;
3767 player->artwork_element =
3768 (level.use_artwork_element[i] ? level.artwork_element[i] :
3769 player->element_nr);
3770 player->use_murphy = FALSE;
3772 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3773 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3775 player->gravity = level.initial_player_gravity[i];
3777 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3779 player->actual_frame_counter = 0;
3781 player->step_counter = 0;
3783 player->last_move_dir = initial_move_dir;
3785 player->is_active = FALSE;
3787 player->is_waiting = FALSE;
3788 player->is_moving = FALSE;
3789 player->is_auto_moving = FALSE;
3790 player->is_digging = FALSE;
3791 player->is_snapping = FALSE;
3792 player->is_collecting = FALSE;
3793 player->is_pushing = FALSE;
3794 player->is_switching = FALSE;
3795 player->is_dropping = FALSE;
3796 player->is_dropping_pressed = FALSE;
3798 player->is_bored = FALSE;
3799 player->is_sleeping = FALSE;
3801 player->frame_counter_bored = -1;
3802 player->frame_counter_sleeping = -1;
3804 player->anim_delay_counter = 0;
3805 player->post_delay_counter = 0;
3807 player->dir_waiting = initial_move_dir;
3808 player->action_waiting = ACTION_DEFAULT;
3809 player->last_action_waiting = ACTION_DEFAULT;
3810 player->special_action_bored = ACTION_DEFAULT;
3811 player->special_action_sleeping = ACTION_DEFAULT;
3813 player->switch_x = -1;
3814 player->switch_y = -1;
3816 player->drop_x = -1;
3817 player->drop_y = -1;
3819 player->show_envelope = 0;
3821 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3823 player->push_delay = -1; /* initialized when pushing starts */
3824 player->push_delay_value = game.initial_push_delay_value;
3826 player->drop_delay = 0;
3827 player->drop_pressed_delay = 0;
3829 player->last_jx = -1;
3830 player->last_jy = -1;
3834 player->shield_normal_time_left = 0;
3835 player->shield_deadly_time_left = 0;
3837 player->inventory_infinite_element = EL_UNDEFINED;
3838 player->inventory_size = 0;
3840 if (level.use_initial_inventory[i])
3842 for (j = 0; j < level.initial_inventory_size[i]; j++)
3844 int element = level.initial_inventory_content[i][j];
3845 int collect_count = element_info[element].collect_count_initial;
3848 if (!IS_CUSTOM_ELEMENT(element))
3851 if (collect_count == 0)
3852 player->inventory_infinite_element = element;
3854 for (k = 0; k < collect_count; k++)
3855 if (player->inventory_size < MAX_INVENTORY_SIZE)
3856 player->inventory_element[player->inventory_size++] = element;
3860 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3861 SnapField(player, 0, 0);
3863 player->LevelSolved = FALSE;
3864 player->GameOver = FALSE;
3866 player->LevelSolved_GameWon = FALSE;
3867 player->LevelSolved_GameEnd = FALSE;
3868 player->LevelSolved_PanelOff = FALSE;
3869 player->LevelSolved_SaveTape = FALSE;
3870 player->LevelSolved_SaveScore = FALSE;
3871 player->LevelSolved_CountingTime = 0;
3872 player->LevelSolved_CountingScore = 0;
3874 map_player_action[i] = i;
3877 network_player_action_received = FALSE;
3879 #if defined(NETWORK_AVALIABLE)
3880 /* initial null action */
3881 if (network_playing)
3882 SendToServer_MovePlayer(MV_NONE);
3891 TimeLeft = level.time;
3894 ScreenMovDir = MV_NONE;
3898 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3900 AllPlayersGone = FALSE;
3902 game.yamyam_content_nr = 0;
3903 game.robot_wheel_active = FALSE;
3904 game.magic_wall_active = FALSE;
3905 game.magic_wall_time_left = 0;
3906 game.light_time_left = 0;
3907 game.timegate_time_left = 0;
3908 game.switchgate_pos = 0;
3909 game.wind_direction = level.wind_direction_initial;
3911 #if !USE_PLAYER_GRAVITY
3912 game.gravity = FALSE;
3913 game.explosions_delayed = TRUE;
3916 game.lenses_time_left = 0;
3917 game.magnify_time_left = 0;
3919 game.ball_state = level.ball_state_initial;
3920 game.ball_content_nr = 0;
3922 game.envelope_active = FALSE;
3924 /* set focus to local player for network games, else to all players */
3925 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3926 game.centered_player_nr_next = game.centered_player_nr;
3927 game.set_centered_player = FALSE;
3929 if (network_playing && tape.recording)
3931 /* store client dependent player focus when recording network games */
3932 tape.centered_player_nr_next = game.centered_player_nr_next;
3933 tape.set_centered_player = TRUE;
3936 for (i = 0; i < NUM_BELTS; i++)
3938 game.belt_dir[i] = MV_NONE;
3939 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3942 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3943 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3945 SCAN_PLAYFIELD(x, y)
3947 Feld[x][y] = level.field[x][y];
3948 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3949 ChangeDelay[x][y] = 0;
3950 ChangePage[x][y] = -1;
3951 #if USE_NEW_CUSTOM_VALUE
3952 CustomValue[x][y] = 0; /* initialized in InitField() */
3954 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3956 WasJustMoving[x][y] = 0;
3957 WasJustFalling[x][y] = 0;
3958 CheckCollision[x][y] = 0;
3959 CheckImpact[x][y] = 0;
3961 Pushed[x][y] = FALSE;
3963 ChangeCount[x][y] = 0;
3964 ChangeEvent[x][y] = -1;
3966 ExplodePhase[x][y] = 0;
3967 ExplodeDelay[x][y] = 0;
3968 ExplodeField[x][y] = EX_TYPE_NONE;
3970 RunnerVisit[x][y] = 0;
3971 PlayerVisit[x][y] = 0;
3974 GfxRandom[x][y] = INIT_GFX_RANDOM();
3975 GfxElement[x][y] = EL_UNDEFINED;
3976 GfxAction[x][y] = ACTION_DEFAULT;
3977 GfxDir[x][y] = MV_NONE;
3978 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3981 SCAN_PLAYFIELD(x, y)
3983 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3985 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3987 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3990 InitField(x, y, TRUE);
3992 ResetGfxAnimation(x, y);
3997 for (i = 0; i < MAX_PLAYERS; i++)
3999 struct PlayerInfo *player = &stored_player[i];
4001 /* set number of special actions for bored and sleeping animation */
4002 player->num_special_action_bored =
4003 get_num_special_action(player->artwork_element,
4004 ACTION_BORING_1, ACTION_BORING_LAST);
4005 player->num_special_action_sleeping =
4006 get_num_special_action(player->artwork_element,
4007 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4010 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4011 emulate_sb ? EMU_SOKOBAN :
4012 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4014 #if USE_NEW_ALL_SLIPPERY
4015 /* initialize type of slippery elements */
4016 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4018 if (!IS_CUSTOM_ELEMENT(i))
4020 /* default: elements slip down either to the left or right randomly */
4021 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4023 /* SP style elements prefer to slip down on the left side */
4024 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4025 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4027 /* BD style elements prefer to slip down on the left side */
4028 if (game.emulation == EMU_BOULDERDASH)
4029 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4034 /* initialize explosion and ignition delay */
4035 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4037 if (!IS_CUSTOM_ELEMENT(i))
4040 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4041 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4042 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4043 int last_phase = (num_phase + 1) * delay;
4044 int half_phase = (num_phase / 2) * delay;
4046 element_info[i].explosion_delay = last_phase - 1;
4047 element_info[i].ignition_delay = half_phase;
4049 if (i == EL_BLACK_ORB)
4050 element_info[i].ignition_delay = 1;
4054 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4055 element_info[i].explosion_delay = 1;
4057 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4058 element_info[i].ignition_delay = 1;
4062 /* correct non-moving belts to start moving left */
4063 for (i = 0; i < NUM_BELTS; i++)
4064 if (game.belt_dir[i] == MV_NONE)
4065 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4067 #if USE_NEW_PLAYER_ASSIGNMENTS
4068 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4069 /* choose default local player */
4070 local_player = &stored_player[0];
4072 for (i = 0; i < MAX_PLAYERS; i++)
4073 stored_player[i].connected = FALSE;
4075 local_player->connected = TRUE;
4076 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4080 /* try to guess locally connected team mode players (needed for correct
4081 assignment of player figures from level to locally playing players) */
4083 for (i = 0; i < MAX_PLAYERS; i++)
4084 if (tape.player_participates[i])
4085 stored_player[i].connected = TRUE;
4087 else if (setup.team_mode && !options.network)
4089 /* try to guess locally connected team mode players (needed for correct
4090 assignment of player figures from level to locally playing players) */
4092 for (i = 0; i < MAX_PLAYERS; i++)
4093 if (setup.input[i].use_joystick ||
4094 setup.input[i].key.left != KSYM_UNDEFINED)
4095 stored_player[i].connected = TRUE;
4099 for (i = 0; i < MAX_PLAYERS; i++)
4100 printf("::: player %d: %s\n", i,
4101 (stored_player[i].connected ? "connected" : "not connected"));
4103 for (i = 0; i < MAX_PLAYERS; i++)
4104 printf("::: player %d: %s\n", i,
4105 (stored_player[i].present ? "present" : "not present"));
4108 /* check if any connected player was not found in playfield */
4109 for (i = 0; i < MAX_PLAYERS; i++)
4111 struct PlayerInfo *player = &stored_player[i];
4113 if (player->connected && !player->present)
4115 struct PlayerInfo *field_player = NULL;
4118 printf("::: looking for field player for player %d ...\n", i);
4121 /* assign first free player found that is present in the playfield */
4123 /* first try: look for unmapped playfield player that is not connected */
4124 if (field_player == NULL)
4125 for (j = 0; j < MAX_PLAYERS; j++)
4126 if (stored_player[j].present &&
4127 !stored_player[j].mapped &&
4128 !stored_player[j].connected)
4129 field_player = &stored_player[j];
4131 /* second try: look for *any* unmapped playfield player */
4132 if (field_player == NULL)
4133 for (j = 0; j < MAX_PLAYERS; j++)
4134 if (stored_player[j].present &&
4135 !stored_player[j].mapped)
4136 field_player = &stored_player[j];
4138 if (field_player != NULL)
4140 int jx = field_player->jx, jy = field_player->jy;
4143 printf("::: found player figure %d\n", field_player->index_nr);
4146 player->present = FALSE;
4147 player->active = FALSE;
4149 field_player->present = TRUE;
4150 field_player->active = TRUE;
4153 player->initial_element = field_player->initial_element;
4154 player->artwork_element = field_player->artwork_element;
4156 player->block_last_field = field_player->block_last_field;
4157 player->block_delay_adjustment = field_player->block_delay_adjustment;
4160 StorePlayer[jx][jy] = field_player->element_nr;
4162 field_player->jx = field_player->last_jx = jx;
4163 field_player->jy = field_player->last_jy = jy;
4165 if (local_player == player)
4166 local_player = field_player;
4168 map_player_action[field_player->index_nr] = i;
4170 field_player->mapped = TRUE;
4173 printf("::: map_player_action[%d] == %d\n",
4174 field_player->index_nr, i);
4179 if (player->connected && player->present)
4180 player->mapped = TRUE;
4185 /* check if any connected player was not found in playfield */
4186 for (i = 0; i < MAX_PLAYERS; i++)
4188 struct PlayerInfo *player = &stored_player[i];
4190 if (player->connected && !player->present)
4192 for (j = 0; j < MAX_PLAYERS; j++)
4194 struct PlayerInfo *field_player = &stored_player[j];
4195 int jx = field_player->jx, jy = field_player->jy;
4197 /* assign first free player found that is present in the playfield */
4198 if (field_player->present && !field_player->connected)
4200 player->present = TRUE;
4201 player->active = TRUE;
4203 field_player->present = FALSE;
4204 field_player->active = FALSE;
4206 player->initial_element = field_player->initial_element;
4207 player->artwork_element = field_player->artwork_element;
4209 player->block_last_field = field_player->block_last_field;
4210 player->block_delay_adjustment = field_player->block_delay_adjustment;
4212 StorePlayer[jx][jy] = player->element_nr;
4214 player->jx = player->last_jx = jx;
4215 player->jy = player->last_jy = jy;
4225 printf("::: local_player->present == %d\n", local_player->present);
4230 /* when playing a tape, eliminate all players who do not participate */
4232 #if USE_NEW_PLAYER_ASSIGNMENTS
4233 for (i = 0; i < MAX_PLAYERS; i++)
4235 if (stored_player[i].active &&
4236 !tape.player_participates[map_player_action[i]])
4238 struct PlayerInfo *player = &stored_player[i];
4239 int jx = player->jx, jy = player->jy;
4241 player->active = FALSE;
4242 StorePlayer[jx][jy] = 0;
4243 Feld[jx][jy] = EL_EMPTY;
4247 for (i = 0; i < MAX_PLAYERS; i++)
4249 if (stored_player[i].active &&
4250 !tape.player_participates[i])
4252 struct PlayerInfo *player = &stored_player[i];
4253 int jx = player->jx, jy = player->jy;
4255 player->active = FALSE;
4256 StorePlayer[jx][jy] = 0;
4257 Feld[jx][jy] = EL_EMPTY;
4262 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4264 /* when in single player mode, eliminate all but the first active player */
4266 for (i = 0; i < MAX_PLAYERS; i++)
4268 if (stored_player[i].active)
4270 for (j = i + 1; j < MAX_PLAYERS; j++)
4272 if (stored_player[j].active)
4274 struct PlayerInfo *player = &stored_player[j];
4275 int jx = player->jx, jy = player->jy;
4277 player->active = FALSE;
4278 player->present = FALSE;
4280 StorePlayer[jx][jy] = 0;
4281 Feld[jx][jy] = EL_EMPTY;
4288 /* when recording the game, store which players take part in the game */
4291 #if USE_NEW_PLAYER_ASSIGNMENTS
4292 for (i = 0; i < MAX_PLAYERS; i++)
4293 if (stored_player[i].connected)
4294 tape.player_participates[i] = TRUE;
4296 for (i = 0; i < MAX_PLAYERS; i++)
4297 if (stored_player[i].active)
4298 tape.player_participates[i] = TRUE;
4304 for (i = 0; i < MAX_PLAYERS; i++)
4306 struct PlayerInfo *player = &stored_player[i];
4308 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4313 if (local_player == player)
4314 printf("Player %d is local player.\n", i+1);
4318 if (BorderElement == EL_EMPTY)
4321 SBX_Right = lev_fieldx - SCR_FIELDX;
4323 SBY_Lower = lev_fieldy - SCR_FIELDY;
4328 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4330 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4333 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4334 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4336 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4337 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4339 /* if local player not found, look for custom element that might create
4340 the player (make some assumptions about the right custom element) */
4341 if (!local_player->present)
4343 int start_x = 0, start_y = 0;
4344 int found_rating = 0;
4345 int found_element = EL_UNDEFINED;
4346 int player_nr = local_player->index_nr;
4348 SCAN_PLAYFIELD(x, y)
4350 int element = Feld[x][y];
4355 if (level.use_start_element[player_nr] &&
4356 level.start_element[player_nr] == element &&
4363 found_element = element;
4366 if (!IS_CUSTOM_ELEMENT(element))
4369 if (CAN_CHANGE(element))
4371 for (i = 0; i < element_info[element].num_change_pages; i++)
4373 /* check for player created from custom element as single target */
4374 content = element_info[element].change_page[i].target_element;
4375 is_player = ELEM_IS_PLAYER(content);
4377 if (is_player && (found_rating < 3 ||
4378 (found_rating == 3 && element < found_element)))
4384 found_element = element;
4389 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4391 /* check for player created from custom element as explosion content */
4392 content = element_info[element].content.e[xx][yy];
4393 is_player = ELEM_IS_PLAYER(content);
4395 if (is_player && (found_rating < 2 ||
4396 (found_rating == 2 && element < found_element)))
4398 start_x = x + xx - 1;
4399 start_y = y + yy - 1;
4402 found_element = element;
4405 if (!CAN_CHANGE(element))
4408 for (i = 0; i < element_info[element].num_change_pages; i++)
4410 /* check for player created from custom element as extended target */
4412 element_info[element].change_page[i].target_content.e[xx][yy];
4414 is_player = ELEM_IS_PLAYER(content);
4416 if (is_player && (found_rating < 1 ||
4417 (found_rating == 1 && element < found_element)))
4419 start_x = x + xx - 1;
4420 start_y = y + yy - 1;
4423 found_element = element;
4429 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4430 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4433 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4434 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4439 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4440 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4441 local_player->jx - MIDPOSX);
4443 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4444 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4445 local_player->jy - MIDPOSY);
4449 /* do not use PLAYING mask for fading out from main screen */
4450 game_status = GAME_MODE_MAIN;
4455 if (!game.restart_level)
4456 CloseDoor(DOOR_CLOSE_1);
4459 if (level_editor_test_game)
4460 FadeSkipNextFadeIn();
4462 FadeSetEnterScreen();
4464 if (level_editor_test_game)
4465 fading = fading_none;
4467 fading = menu.destination;
4471 FadeOut(REDRAW_FIELD);
4474 FadeOut(REDRAW_FIELD);
4478 game_status = GAME_MODE_PLAYING;
4481 /* !!! FIX THIS (START) !!! */
4482 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4484 InitGameEngine_EM();
4486 /* blit playfield from scroll buffer to normal back buffer for fading in */
4487 BlitScreenToBitmap_EM(backbuffer);
4489 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4491 InitGameEngine_SP();
4493 /* blit playfield from scroll buffer to normal back buffer for fading in */
4494 BlitScreenToBitmap_SP(backbuffer);
4501 /* after drawing the level, correct some elements */
4502 if (game.timegate_time_left == 0)
4503 CloseAllOpenTimegates();
4505 /* blit playfield from scroll buffer to normal back buffer for fading in */
4506 if (setup.soft_scrolling)
4507 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4509 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4511 /* !!! FIX THIS (END) !!! */
4514 FadeIn(REDRAW_FIELD);
4517 FadeIn(REDRAW_FIELD);
4522 if (!game.restart_level)
4524 /* copy default game door content to main double buffer */
4525 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4526 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4529 SetPanelBackground();
4530 SetDrawBackgroundMask(REDRAW_DOOR_1);
4533 UpdateAndDisplayGameControlValues();
4535 UpdateGameDoorValues();
4536 DrawGameDoorValues();
4539 if (!game.restart_level)
4543 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4544 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4545 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4549 /* copy actual game door content to door double buffer for OpenDoor() */
4550 BlitBitmap(drawto, bitmap_db_door,
4551 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4553 OpenDoor(DOOR_OPEN_ALL);
4555 PlaySound(SND_GAME_STARTING);
4557 if (setup.sound_music)
4560 KeyboardAutoRepeatOffUnlessAutoplay();
4564 for (i = 0; i < MAX_PLAYERS; i++)
4565 printf("Player %d %sactive.\n",
4566 i + 1, (stored_player[i].active ? "" : "not "));
4577 game.restart_level = FALSE;
4580 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4582 /* this is used for non-R'n'D game engines to update certain engine values */
4584 /* needed to determine if sounds are played within the visible screen area */
4585 scroll_x = actual_scroll_x;
4586 scroll_y = actual_scroll_y;
4589 void InitMovDir(int x, int y)
4591 int i, element = Feld[x][y];
4592 static int xy[4][2] =
4599 static int direction[3][4] =
4601 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4602 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4603 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4612 Feld[x][y] = EL_BUG;
4613 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4616 case EL_SPACESHIP_RIGHT:
4617 case EL_SPACESHIP_UP:
4618 case EL_SPACESHIP_LEFT:
4619 case EL_SPACESHIP_DOWN:
4620 Feld[x][y] = EL_SPACESHIP;
4621 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4624 case EL_BD_BUTTERFLY_RIGHT:
4625 case EL_BD_BUTTERFLY_UP:
4626 case EL_BD_BUTTERFLY_LEFT:
4627 case EL_BD_BUTTERFLY_DOWN:
4628 Feld[x][y] = EL_BD_BUTTERFLY;
4629 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4632 case EL_BD_FIREFLY_RIGHT:
4633 case EL_BD_FIREFLY_UP:
4634 case EL_BD_FIREFLY_LEFT:
4635 case EL_BD_FIREFLY_DOWN:
4636 Feld[x][y] = EL_BD_FIREFLY;
4637 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4640 case EL_PACMAN_RIGHT:
4642 case EL_PACMAN_LEFT:
4643 case EL_PACMAN_DOWN:
4644 Feld[x][y] = EL_PACMAN;
4645 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4648 case EL_YAMYAM_LEFT:
4649 case EL_YAMYAM_RIGHT:
4651 case EL_YAMYAM_DOWN:
4652 Feld[x][y] = EL_YAMYAM;
4653 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4656 case EL_SP_SNIKSNAK:
4657 MovDir[x][y] = MV_UP;
4660 case EL_SP_ELECTRON:
4661 MovDir[x][y] = MV_LEFT;
4668 Feld[x][y] = EL_MOLE;
4669 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4673 if (IS_CUSTOM_ELEMENT(element))
4675 struct ElementInfo *ei = &element_info[element];
4676 int move_direction_initial = ei->move_direction_initial;
4677 int move_pattern = ei->move_pattern;
4679 if (move_direction_initial == MV_START_PREVIOUS)
4681 if (MovDir[x][y] != MV_NONE)
4684 move_direction_initial = MV_START_AUTOMATIC;
4687 if (move_direction_initial == MV_START_RANDOM)
4688 MovDir[x][y] = 1 << RND(4);
4689 else if (move_direction_initial & MV_ANY_DIRECTION)
4690 MovDir[x][y] = move_direction_initial;
4691 else if (move_pattern == MV_ALL_DIRECTIONS ||
4692 move_pattern == MV_TURNING_LEFT ||
4693 move_pattern == MV_TURNING_RIGHT ||
4694 move_pattern == MV_TURNING_LEFT_RIGHT ||
4695 move_pattern == MV_TURNING_RIGHT_LEFT ||
4696 move_pattern == MV_TURNING_RANDOM)
4697 MovDir[x][y] = 1 << RND(4);
4698 else if (move_pattern == MV_HORIZONTAL)
4699 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4700 else if (move_pattern == MV_VERTICAL)
4701 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4702 else if (move_pattern & MV_ANY_DIRECTION)
4703 MovDir[x][y] = element_info[element].move_pattern;
4704 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4705 move_pattern == MV_ALONG_RIGHT_SIDE)
4707 /* use random direction as default start direction */
4708 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4709 MovDir[x][y] = 1 << RND(4);
4711 for (i = 0; i < NUM_DIRECTIONS; i++)
4713 int x1 = x + xy[i][0];
4714 int y1 = y + xy[i][1];
4716 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4718 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4719 MovDir[x][y] = direction[0][i];
4721 MovDir[x][y] = direction[1][i];
4730 MovDir[x][y] = 1 << RND(4);
4732 if (element != EL_BUG &&
4733 element != EL_SPACESHIP &&
4734 element != EL_BD_BUTTERFLY &&
4735 element != EL_BD_FIREFLY)
4738 for (i = 0; i < NUM_DIRECTIONS; i++)
4740 int x1 = x + xy[i][0];
4741 int y1 = y + xy[i][1];
4743 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4745 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4747 MovDir[x][y] = direction[0][i];
4750 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4751 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4753 MovDir[x][y] = direction[1][i];
4762 GfxDir[x][y] = MovDir[x][y];
4765 void InitAmoebaNr(int x, int y)
4768 int group_nr = AmoebeNachbarNr(x, y);
4772 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4774 if (AmoebaCnt[i] == 0)
4782 AmoebaNr[x][y] = group_nr;
4783 AmoebaCnt[group_nr]++;
4784 AmoebaCnt2[group_nr]++;
4787 static void PlayerWins(struct PlayerInfo *player)
4789 player->LevelSolved = TRUE;
4790 player->GameOver = TRUE;
4792 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4793 level.native_em_level->lev->score : player->score);
4795 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4796 player->LevelSolved_CountingScore = player->score_final;
4801 static int time, time_final;
4802 static int score, score_final;
4803 static int game_over_delay_1 = 0;
4804 static int game_over_delay_2 = 0;
4805 int game_over_delay_value_1 = 50;
4806 int game_over_delay_value_2 = 50;
4808 if (!local_player->LevelSolved_GameWon)
4812 /* do not start end game actions before the player stops moving (to exit) */
4813 if (local_player->MovPos)
4816 local_player->LevelSolved_GameWon = TRUE;
4817 local_player->LevelSolved_SaveTape = tape.recording;
4818 local_player->LevelSolved_SaveScore = !tape.playing;
4820 if (tape.auto_play) /* tape might already be stopped here */
4821 tape.auto_play_level_solved = TRUE;
4827 game_over_delay_1 = game_over_delay_value_1;
4828 game_over_delay_2 = game_over_delay_value_2;
4830 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4831 score = score_final = local_player->score_final;
4836 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4838 else if (level.time == 0 && TimePlayed < 999)
4841 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4844 local_player->score_final = score_final;
4846 if (level_editor_test_game)
4849 score = score_final;
4852 local_player->LevelSolved_CountingTime = time;
4853 local_player->LevelSolved_CountingScore = score;
4855 game_panel_controls[GAME_PANEL_TIME].value = time;
4856 game_panel_controls[GAME_PANEL_SCORE].value = score;
4858 DisplayGameControlValues();
4860 DrawGameValue_Time(time);
4861 DrawGameValue_Score(score);
4865 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4867 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4869 /* close exit door after last player */
4870 if ((AllPlayersGone &&
4871 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4872 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4873 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4874 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4875 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4877 int element = Feld[ExitX][ExitY];
4880 if (element == EL_EM_EXIT_OPEN ||
4881 element == EL_EM_STEEL_EXIT_OPEN)
4888 Feld[ExitX][ExitY] =
4889 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4890 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4891 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4892 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4893 EL_EM_STEEL_EXIT_CLOSING);
4895 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4899 /* player disappears */
4900 DrawLevelField(ExitX, ExitY);
4903 for (i = 0; i < MAX_PLAYERS; i++)
4905 struct PlayerInfo *player = &stored_player[i];
4907 if (player->present)
4909 RemovePlayer(player);
4911 /* player disappears */
4912 DrawLevelField(player->jx, player->jy);
4917 PlaySound(SND_GAME_WINNING);
4920 if (game_over_delay_1 > 0)
4922 game_over_delay_1--;
4927 if (time != time_final)
4929 int time_to_go = ABS(time_final - time);
4930 int time_count_dir = (time < time_final ? +1 : -1);
4931 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4933 time += time_count_steps * time_count_dir;
4934 score += time_count_steps * level.score[SC_TIME_BONUS];
4937 local_player->LevelSolved_CountingTime = time;
4938 local_player->LevelSolved_CountingScore = score;
4940 game_panel_controls[GAME_PANEL_TIME].value = time;
4941 game_panel_controls[GAME_PANEL_SCORE].value = score;
4943 DisplayGameControlValues();
4945 DrawGameValue_Time(time);
4946 DrawGameValue_Score(score);
4949 if (time == time_final)
4950 StopSound(SND_GAME_LEVELTIME_BONUS);
4951 else if (setup.sound_loops)
4952 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4954 PlaySound(SND_GAME_LEVELTIME_BONUS);
4959 local_player->LevelSolved_PanelOff = TRUE;
4961 if (game_over_delay_2 > 0)
4963 game_over_delay_2--;
4976 boolean raise_level = FALSE;
4978 local_player->LevelSolved_GameEnd = TRUE;
4980 CloseDoor(DOOR_CLOSE_1);
4982 if (local_player->LevelSolved_SaveTape)
4989 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4991 SaveTape(tape.level_nr); /* ask to save tape */
4995 if (level_editor_test_game)
4997 game_status = GAME_MODE_MAIN;
5000 DrawAndFadeInMainMenu(REDRAW_FIELD);
5008 if (!local_player->LevelSolved_SaveScore)
5011 FadeOut(REDRAW_FIELD);
5014 game_status = GAME_MODE_MAIN;
5016 DrawAndFadeInMainMenu(REDRAW_FIELD);
5021 if (level_nr == leveldir_current->handicap_level)
5023 leveldir_current->handicap_level++;
5024 SaveLevelSetup_SeriesInfo();
5027 if (level_nr < leveldir_current->last_level)
5028 raise_level = TRUE; /* advance to next level */
5030 if ((hi_pos = NewHiScore()) >= 0)
5032 game_status = GAME_MODE_SCORES;
5034 DrawHallOfFame(hi_pos);
5045 FadeOut(REDRAW_FIELD);
5048 game_status = GAME_MODE_MAIN;
5056 DrawAndFadeInMainMenu(REDRAW_FIELD);
5065 LoadScore(level_nr);
5067 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5068 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5071 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5073 if (local_player->score_final > highscore[k].Score)
5075 /* player has made it to the hall of fame */
5077 if (k < MAX_SCORE_ENTRIES - 1)
5079 int m = MAX_SCORE_ENTRIES - 1;
5082 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5083 if (strEqual(setup.player_name, highscore[l].Name))
5085 if (m == k) /* player's new highscore overwrites his old one */
5089 for (l = m; l > k; l--)
5091 strcpy(highscore[l].Name, highscore[l - 1].Name);
5092 highscore[l].Score = highscore[l - 1].Score;
5099 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5100 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5101 highscore[k].Score = local_player->score_final;
5107 else if (!strncmp(setup.player_name, highscore[k].Name,
5108 MAX_PLAYER_NAME_LEN))
5109 break; /* player already there with a higher score */
5115 SaveScore(level_nr);
5120 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5122 int element = Feld[x][y];
5123 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5124 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5125 int horiz_move = (dx != 0);
5126 int sign = (horiz_move ? dx : dy);
5127 int step = sign * element_info[element].move_stepsize;
5129 /* special values for move stepsize for spring and things on conveyor belt */
5132 if (CAN_FALL(element) &&
5133 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5134 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5135 else if (element == EL_SPRING)
5136 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5142 inline static int getElementMoveStepsize(int x, int y)
5144 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5147 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5149 if (player->GfxAction != action || player->GfxDir != dir)
5152 printf("Player frame reset! (%d => %d, %d => %d)\n",
5153 player->GfxAction, action, player->GfxDir, dir);
5156 player->GfxAction = action;
5157 player->GfxDir = dir;
5159 player->StepFrame = 0;
5163 #if USE_GFX_RESET_GFX_ANIMATION
5164 static void ResetGfxFrame(int x, int y, boolean redraw)
5166 int element = Feld[x][y];
5167 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5168 int last_gfx_frame = GfxFrame[x][y];
5170 if (graphic_info[graphic].anim_global_sync)
5171 GfxFrame[x][y] = FrameCounter;
5172 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5173 GfxFrame[x][y] = CustomValue[x][y];
5174 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5175 GfxFrame[x][y] = element_info[element].collect_score;
5176 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5177 GfxFrame[x][y] = ChangeDelay[x][y];
5179 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5180 DrawLevelGraphicAnimation(x, y, graphic);
5184 static void ResetGfxAnimation(int x, int y)
5186 GfxAction[x][y] = ACTION_DEFAULT;
5187 GfxDir[x][y] = MovDir[x][y];
5190 #if USE_GFX_RESET_GFX_ANIMATION
5191 ResetGfxFrame(x, y, FALSE);
5195 static void ResetRandomAnimationValue(int x, int y)
5197 GfxRandom[x][y] = INIT_GFX_RANDOM();
5200 void InitMovingField(int x, int y, int direction)
5202 int element = Feld[x][y];
5203 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5204 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5207 boolean is_moving_before, is_moving_after;
5209 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5212 /* check if element was/is moving or being moved before/after mode change */
5215 is_moving_before = (WasJustMoving[x][y] != 0);
5217 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5218 is_moving_before = WasJustMoving[x][y];
5221 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5223 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5225 /* reset animation only for moving elements which change direction of moving
5226 or which just started or stopped moving
5227 (else CEs with property "can move" / "not moving" are reset each frame) */
5228 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5230 if (is_moving_before != is_moving_after ||
5231 direction != MovDir[x][y])
5232 ResetGfxAnimation(x, y);
5234 if ((is_moving_before || is_moving_after) && !continues_moving)
5235 ResetGfxAnimation(x, y);
5238 if (!continues_moving)
5239 ResetGfxAnimation(x, y);
5242 MovDir[x][y] = direction;
5243 GfxDir[x][y] = direction;
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5246 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5247 direction == MV_DOWN && CAN_FALL(element) ?
5248 ACTION_FALLING : ACTION_MOVING);
5250 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5251 ACTION_FALLING : ACTION_MOVING);
5254 /* this is needed for CEs with property "can move" / "not moving" */
5256 if (is_moving_after)
5258 if (Feld[newx][newy] == EL_EMPTY)
5259 Feld[newx][newy] = EL_BLOCKED;
5261 MovDir[newx][newy] = MovDir[x][y];
5263 #if USE_NEW_CUSTOM_VALUE
5264 CustomValue[newx][newy] = CustomValue[x][y];
5267 GfxFrame[newx][newy] = GfxFrame[x][y];
5268 GfxRandom[newx][newy] = GfxRandom[x][y];
5269 GfxAction[newx][newy] = GfxAction[x][y];
5270 GfxDir[newx][newy] = GfxDir[x][y];
5274 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5276 int direction = MovDir[x][y];
5277 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5278 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5284 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5286 int oldx = x, oldy = y;
5287 int direction = MovDir[x][y];
5289 if (direction == MV_LEFT)
5291 else if (direction == MV_RIGHT)
5293 else if (direction == MV_UP)
5295 else if (direction == MV_DOWN)
5298 *comes_from_x = oldx;
5299 *comes_from_y = oldy;
5302 int MovingOrBlocked2Element(int x, int y)
5304 int element = Feld[x][y];
5306 if (element == EL_BLOCKED)
5310 Blocked2Moving(x, y, &oldx, &oldy);
5311 return Feld[oldx][oldy];
5317 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5319 /* like MovingOrBlocked2Element(), but if element is moving
5320 and (x,y) is the field the moving element is just leaving,
5321 return EL_BLOCKED instead of the element value */
5322 int element = Feld[x][y];
5324 if (IS_MOVING(x, y))
5326 if (element == EL_BLOCKED)
5330 Blocked2Moving(x, y, &oldx, &oldy);
5331 return Feld[oldx][oldy];
5340 static void RemoveField(int x, int y)
5342 Feld[x][y] = EL_EMPTY;
5348 #if USE_NEW_CUSTOM_VALUE
5349 CustomValue[x][y] = 0;
5353 ChangeDelay[x][y] = 0;
5354 ChangePage[x][y] = -1;
5355 Pushed[x][y] = FALSE;
5358 ExplodeField[x][y] = EX_TYPE_NONE;
5361 GfxElement[x][y] = EL_UNDEFINED;
5362 GfxAction[x][y] = ACTION_DEFAULT;
5363 GfxDir[x][y] = MV_NONE;
5365 /* !!! this would prevent the removed tile from being redrawn !!! */
5366 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5370 void RemoveMovingField(int x, int y)
5372 int oldx = x, oldy = y, newx = x, newy = y;
5373 int element = Feld[x][y];
5374 int next_element = EL_UNDEFINED;
5376 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5379 if (IS_MOVING(x, y))
5381 Moving2Blocked(x, y, &newx, &newy);
5383 if (Feld[newx][newy] != EL_BLOCKED)
5385 /* element is moving, but target field is not free (blocked), but
5386 already occupied by something different (example: acid pool);
5387 in this case, only remove the moving field, but not the target */
5389 RemoveField(oldx, oldy);
5391 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5393 TEST_DrawLevelField(oldx, oldy);
5398 else if (element == EL_BLOCKED)
5400 Blocked2Moving(x, y, &oldx, &oldy);
5401 if (!IS_MOVING(oldx, oldy))
5405 if (element == EL_BLOCKED &&
5406 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5407 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5408 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5409 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5410 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5411 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5412 next_element = get_next_element(Feld[oldx][oldy]);
5414 RemoveField(oldx, oldy);
5415 RemoveField(newx, newy);
5417 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5419 if (next_element != EL_UNDEFINED)
5420 Feld[oldx][oldy] = next_element;
5422 TEST_DrawLevelField(oldx, oldy);
5423 TEST_DrawLevelField(newx, newy);
5426 void DrawDynamite(int x, int y)
5428 int sx = SCREENX(x), sy = SCREENY(y);
5429 int graphic = el2img(Feld[x][y]);
5432 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5435 if (IS_WALKABLE_INSIDE(Back[x][y]))
5439 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5440 else if (Store[x][y])
5441 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5443 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5445 if (Back[x][y] || Store[x][y])
5446 DrawGraphicThruMask(sx, sy, graphic, frame);
5448 DrawGraphic(sx, sy, graphic, frame);
5451 void CheckDynamite(int x, int y)
5453 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5457 if (MovDelay[x][y] != 0)
5460 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5466 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5471 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5473 boolean num_checked_players = 0;
5476 for (i = 0; i < MAX_PLAYERS; i++)
5478 if (stored_player[i].active)
5480 int sx = stored_player[i].jx;
5481 int sy = stored_player[i].jy;
5483 if (num_checked_players == 0)
5490 *sx1 = MIN(*sx1, sx);
5491 *sy1 = MIN(*sy1, sy);
5492 *sx2 = MAX(*sx2, sx);
5493 *sy2 = MAX(*sy2, sy);
5496 num_checked_players++;
5501 static boolean checkIfAllPlayersFitToScreen_RND()
5503 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5505 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5507 return (sx2 - sx1 < SCR_FIELDX &&
5508 sy2 - sy1 < SCR_FIELDY);
5511 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5513 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5515 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5517 *sx = (sx1 + sx2) / 2;
5518 *sy = (sy1 + sy2) / 2;
5521 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5522 boolean center_screen, boolean quick_relocation)
5524 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5525 boolean no_delay = (tape.warp_forward);
5526 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5527 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5529 if (quick_relocation)
5531 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5533 if (!level.shifted_relocation || center_screen)
5535 /* quick relocation (without scrolling), with centering of screen */
5537 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5538 x > SBX_Right + MIDPOSX ? SBX_Right :
5541 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5542 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5547 /* quick relocation (without scrolling), but do not center screen */
5549 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5550 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5553 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5554 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5557 int offset_x = x + (scroll_x - center_scroll_x);
5558 int offset_y = y + (scroll_y - center_scroll_y);
5560 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5561 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5562 offset_x - MIDPOSX);
5564 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5565 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5566 offset_y - MIDPOSY);
5572 if (!level.shifted_relocation || center_screen)
5574 /* quick relocation (without scrolling), with centering of screen */
5576 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5577 x > SBX_Right + MIDPOSX ? SBX_Right :
5580 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5581 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5586 /* quick relocation (without scrolling), but do not center screen */
5588 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5589 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5592 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5593 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5596 int offset_x = x + (scroll_x - center_scroll_x);
5597 int offset_y = y + (scroll_y - center_scroll_y);
5599 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5600 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5601 offset_x - MIDPOSX);
5603 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5604 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5605 offset_y - MIDPOSY);
5608 /* quick relocation (without scrolling), inside visible screen area */
5610 int offset = game.scroll_delay_value;
5612 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5613 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5614 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5616 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5617 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5618 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5620 /* don't scroll over playfield boundaries */
5621 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5622 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5624 /* don't scroll over playfield boundaries */
5625 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5626 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5630 RedrawPlayfield(TRUE, 0,0,0,0);
5635 int scroll_xx, scroll_yy;
5637 if (!level.shifted_relocation || center_screen)
5639 /* visible relocation (with scrolling), with centering of screen */
5641 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5642 x > SBX_Right + MIDPOSX ? SBX_Right :
5645 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5646 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5651 /* visible relocation (with scrolling), but do not center screen */
5653 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5654 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5657 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5658 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5661 int offset_x = x + (scroll_x - center_scroll_x);
5662 int offset_y = y + (scroll_y - center_scroll_y);
5664 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5665 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5666 offset_x - MIDPOSX);
5668 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5669 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5670 offset_y - MIDPOSY);
5675 /* visible relocation (with scrolling), with centering of screen */
5677 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5678 x > SBX_Right + MIDPOSX ? SBX_Right :
5681 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5682 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5686 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5688 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5691 int fx = FX, fy = FY;
5693 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5694 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5696 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5702 fx += dx * TILEX / 2;
5703 fy += dy * TILEY / 2;
5705 ScrollLevel(dx, dy);
5708 /* scroll in two steps of half tile size to make things smoother */
5709 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5711 Delay(wait_delay_value);
5713 /* scroll second step to align at full tile size */
5715 Delay(wait_delay_value);
5720 Delay(wait_delay_value);
5724 void RelocatePlayer(int jx, int jy, int el_player_raw)
5726 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5727 int player_nr = GET_PLAYER_NR(el_player);
5728 struct PlayerInfo *player = &stored_player[player_nr];
5729 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5730 boolean no_delay = (tape.warp_forward);
5731 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5732 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5733 int old_jx = player->jx;
5734 int old_jy = player->jy;
5735 int old_element = Feld[old_jx][old_jy];
5736 int element = Feld[jx][jy];
5737 boolean player_relocated = (old_jx != jx || old_jy != jy);
5739 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5740 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5741 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5742 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5743 int leave_side_horiz = move_dir_horiz;
5744 int leave_side_vert = move_dir_vert;
5745 int enter_side = enter_side_horiz | enter_side_vert;
5746 int leave_side = leave_side_horiz | leave_side_vert;
5748 if (player->GameOver) /* do not reanimate dead player */
5751 if (!player_relocated) /* no need to relocate the player */
5754 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5756 RemoveField(jx, jy); /* temporarily remove newly placed player */
5757 DrawLevelField(jx, jy);
5760 if (player->present)
5762 while (player->MovPos)
5764 ScrollPlayer(player, SCROLL_GO_ON);
5765 ScrollScreen(NULL, SCROLL_GO_ON);
5767 AdvanceFrameAndPlayerCounters(player->index_nr);
5772 Delay(wait_delay_value);
5775 DrawPlayer(player); /* needed here only to cleanup last field */
5776 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5778 player->is_moving = FALSE;
5781 if (IS_CUSTOM_ELEMENT(old_element))
5782 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5784 player->index_bit, leave_side);
5786 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5788 player->index_bit, leave_side);
5790 Feld[jx][jy] = el_player;
5791 InitPlayerField(jx, jy, el_player, TRUE);
5793 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5794 possible that the relocation target field did not contain a player element,
5795 but a walkable element, to which the new player was relocated -- in this
5796 case, restore that (already initialized!) element on the player field */
5797 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5799 Feld[jx][jy] = element; /* restore previously existing element */
5801 /* !!! do not initialize already initialized element a second time !!! */
5802 /* (this causes at least problems with "element creation" CE trigger for
5803 already existing elements, and existing Sokoban fields counted twice) */
5804 InitField(jx, jy, FALSE);
5808 /* only visually relocate centered player */
5809 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5810 FALSE, level.instant_relocation);
5812 TestIfPlayerTouchesBadThing(jx, jy);
5813 TestIfPlayerTouchesCustomElement(jx, jy);
5815 if (IS_CUSTOM_ELEMENT(element))
5816 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5817 player->index_bit, enter_side);
5819 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5820 player->index_bit, enter_side);
5823 if (player->is_switching)
5825 /* ensure that relocation while still switching an element does not cause
5826 a new element to be treated as also switched directly after relocation
5827 (this is important for teleporter switches that teleport the player to
5828 a place where another teleporter switch is in the same direction, which
5829 would then incorrectly be treated as immediately switched before the
5830 direction key that caused the switch was released) */
5832 player->switch_x += jx - old_jx;
5833 player->switch_y += jy - old_jy;
5838 void Explode(int ex, int ey, int phase, int mode)
5844 /* !!! eliminate this variable !!! */
5845 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5847 if (game.explosions_delayed)
5849 ExplodeField[ex][ey] = mode;
5853 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5855 int center_element = Feld[ex][ey];
5856 int artwork_element, explosion_element; /* set these values later */
5859 /* --- This is only really needed (and now handled) in "Impact()". --- */
5860 /* do not explode moving elements that left the explode field in time */
5861 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5862 center_element == EL_EMPTY &&
5863 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5868 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5869 if (mode == EX_TYPE_NORMAL ||
5870 mode == EX_TYPE_CENTER ||
5871 mode == EX_TYPE_CROSS)
5872 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5875 /* remove things displayed in background while burning dynamite */
5876 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5879 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5881 /* put moving element to center field (and let it explode there) */
5882 center_element = MovingOrBlocked2Element(ex, ey);
5883 RemoveMovingField(ex, ey);
5884 Feld[ex][ey] = center_element;
5887 /* now "center_element" is finally determined -- set related values now */
5888 artwork_element = center_element; /* for custom player artwork */
5889 explosion_element = center_element; /* for custom player artwork */
5891 if (IS_PLAYER(ex, ey))
5893 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5895 artwork_element = stored_player[player_nr].artwork_element;
5897 if (level.use_explosion_element[player_nr])
5899 explosion_element = level.explosion_element[player_nr];
5900 artwork_element = explosion_element;
5905 if (mode == EX_TYPE_NORMAL ||
5906 mode == EX_TYPE_CENTER ||
5907 mode == EX_TYPE_CROSS)
5908 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5911 last_phase = element_info[explosion_element].explosion_delay + 1;
5913 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5915 int xx = x - ex + 1;
5916 int yy = y - ey + 1;
5919 if (!IN_LEV_FIELD(x, y) ||
5920 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5921 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5924 element = Feld[x][y];
5926 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5928 element = MovingOrBlocked2Element(x, y);
5930 if (!IS_EXPLOSION_PROOF(element))
5931 RemoveMovingField(x, y);
5934 /* indestructible elements can only explode in center (but not flames) */
5935 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5936 mode == EX_TYPE_BORDER)) ||
5937 element == EL_FLAMES)
5940 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5941 behaviour, for example when touching a yamyam that explodes to rocks
5942 with active deadly shield, a rock is created under the player !!! */
5943 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5945 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5946 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5947 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5949 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5952 if (IS_ACTIVE_BOMB(element))
5954 /* re-activate things under the bomb like gate or penguin */
5955 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5962 /* save walkable background elements while explosion on same tile */
5963 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5964 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5965 Back[x][y] = element;
5967 /* ignite explodable elements reached by other explosion */
5968 if (element == EL_EXPLOSION)
5969 element = Store2[x][y];
5971 if (AmoebaNr[x][y] &&
5972 (element == EL_AMOEBA_FULL ||
5973 element == EL_BD_AMOEBA ||
5974 element == EL_AMOEBA_GROWING))
5976 AmoebaCnt[AmoebaNr[x][y]]--;
5977 AmoebaCnt2[AmoebaNr[x][y]]--;
5982 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5984 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5986 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5988 if (PLAYERINFO(ex, ey)->use_murphy)
5989 Store[x][y] = EL_EMPTY;
5992 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5993 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5994 else if (ELEM_IS_PLAYER(center_element))
5995 Store[x][y] = EL_EMPTY;
5996 else if (center_element == EL_YAMYAM)
5997 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5998 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5999 Store[x][y] = element_info[center_element].content.e[xx][yy];
6001 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6002 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6003 otherwise) -- FIX THIS !!! */
6004 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6005 Store[x][y] = element_info[element].content.e[1][1];
6007 else if (!CAN_EXPLODE(element))
6008 Store[x][y] = element_info[element].content.e[1][1];
6011 Store[x][y] = EL_EMPTY;
6013 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6014 center_element == EL_AMOEBA_TO_DIAMOND)
6015 Store2[x][y] = element;
6017 Feld[x][y] = EL_EXPLOSION;
6018 GfxElement[x][y] = artwork_element;
6020 ExplodePhase[x][y] = 1;
6021 ExplodeDelay[x][y] = last_phase;
6026 if (center_element == EL_YAMYAM)
6027 game.yamyam_content_nr =
6028 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6040 GfxFrame[x][y] = 0; /* restart explosion animation */
6042 last_phase = ExplodeDelay[x][y];
6044 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6048 /* activate this even in non-DEBUG version until cause for crash in
6049 getGraphicAnimationFrame() (see below) is found and eliminated */
6055 /* this can happen if the player leaves an explosion just in time */
6056 if (GfxElement[x][y] == EL_UNDEFINED)
6057 GfxElement[x][y] = EL_EMPTY;
6059 if (GfxElement[x][y] == EL_UNDEFINED)
6062 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6063 printf("Explode(): This should never happen!\n");
6066 GfxElement[x][y] = EL_EMPTY;
6072 border_element = Store2[x][y];
6073 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6074 border_element = StorePlayer[x][y];
6076 if (phase == element_info[border_element].ignition_delay ||
6077 phase == last_phase)
6079 boolean border_explosion = FALSE;
6081 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6082 !PLAYER_EXPLOSION_PROTECTED(x, y))
6084 KillPlayerUnlessExplosionProtected(x, y);
6085 border_explosion = TRUE;
6087 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6089 Feld[x][y] = Store2[x][y];
6092 border_explosion = TRUE;
6094 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6096 AmoebeUmwandeln(x, y);
6098 border_explosion = TRUE;
6101 /* if an element just explodes due to another explosion (chain-reaction),
6102 do not immediately end the new explosion when it was the last frame of
6103 the explosion (as it would be done in the following "if"-statement!) */
6104 if (border_explosion && phase == last_phase)
6108 if (phase == last_phase)
6112 element = Feld[x][y] = Store[x][y];
6113 Store[x][y] = Store2[x][y] = 0;
6114 GfxElement[x][y] = EL_UNDEFINED;
6116 /* player can escape from explosions and might therefore be still alive */
6117 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6118 element <= EL_PLAYER_IS_EXPLODING_4)
6120 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6121 int explosion_element = EL_PLAYER_1 + player_nr;
6122 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6123 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6125 if (level.use_explosion_element[player_nr])
6126 explosion_element = level.explosion_element[player_nr];
6128 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6129 element_info[explosion_element].content.e[xx][yy]);
6132 /* restore probably existing indestructible background element */
6133 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6134 element = Feld[x][y] = Back[x][y];
6137 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6138 GfxDir[x][y] = MV_NONE;
6139 ChangeDelay[x][y] = 0;
6140 ChangePage[x][y] = -1;
6142 #if USE_NEW_CUSTOM_VALUE
6143 CustomValue[x][y] = 0;
6146 InitField_WithBug2(x, y, FALSE);
6148 TEST_DrawLevelField(x, y);
6150 TestIfElementTouchesCustomElement(x, y);
6152 if (GFX_CRUMBLED(element))
6153 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6155 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6156 StorePlayer[x][y] = 0;
6158 if (ELEM_IS_PLAYER(element))
6159 RelocatePlayer(x, y, element);
6161 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6163 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6164 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6167 TEST_DrawLevelFieldCrumbledSand(x, y);
6169 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6171 DrawLevelElement(x, y, Back[x][y]);
6172 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6174 else if (IS_WALKABLE_UNDER(Back[x][y]))
6176 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6177 DrawLevelElementThruMask(x, y, Back[x][y]);
6179 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6180 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6184 void DynaExplode(int ex, int ey)
6187 int dynabomb_element = Feld[ex][ey];
6188 int dynabomb_size = 1;
6189 boolean dynabomb_xl = FALSE;
6190 struct PlayerInfo *player;
6191 static int xy[4][2] =
6199 if (IS_ACTIVE_BOMB(dynabomb_element))
6201 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6202 dynabomb_size = player->dynabomb_size;
6203 dynabomb_xl = player->dynabomb_xl;
6204 player->dynabombs_left++;
6207 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6209 for (i = 0; i < NUM_DIRECTIONS; i++)
6211 for (j = 1; j <= dynabomb_size; j++)
6213 int x = ex + j * xy[i][0];
6214 int y = ey + j * xy[i][1];
6217 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6220 element = Feld[x][y];
6222 /* do not restart explosions of fields with active bombs */
6223 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6226 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6228 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6229 !IS_DIGGABLE(element) && !dynabomb_xl)
6235 void Bang(int x, int y)
6237 int element = MovingOrBlocked2Element(x, y);
6238 int explosion_type = EX_TYPE_NORMAL;
6240 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6242 struct PlayerInfo *player = PLAYERINFO(x, y);
6244 #if USE_FIX_CE_ACTION_WITH_PLAYER
6245 element = Feld[x][y] = player->initial_element;
6247 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6248 player->element_nr);
6251 if (level.use_explosion_element[player->index_nr])
6253 int explosion_element = level.explosion_element[player->index_nr];
6255 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6256 explosion_type = EX_TYPE_CROSS;
6257 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6258 explosion_type = EX_TYPE_CENTER;
6266 case EL_BD_BUTTERFLY:
6269 case EL_DARK_YAMYAM:
6273 RaiseScoreElement(element);
6276 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6277 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6278 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6279 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6280 case EL_DYNABOMB_INCREASE_NUMBER:
6281 case EL_DYNABOMB_INCREASE_SIZE:
6282 case EL_DYNABOMB_INCREASE_POWER:
6283 explosion_type = EX_TYPE_DYNA;
6286 case EL_DC_LANDMINE:
6288 case EL_EM_EXIT_OPEN:
6289 case EL_EM_STEEL_EXIT_OPEN:
6291 explosion_type = EX_TYPE_CENTER;
6296 case EL_LAMP_ACTIVE:
6297 case EL_AMOEBA_TO_DIAMOND:
6298 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6299 explosion_type = EX_TYPE_CENTER;
6303 if (element_info[element].explosion_type == EXPLODES_CROSS)
6304 explosion_type = EX_TYPE_CROSS;
6305 else if (element_info[element].explosion_type == EXPLODES_1X1)
6306 explosion_type = EX_TYPE_CENTER;
6310 if (explosion_type == EX_TYPE_DYNA)
6313 Explode(x, y, EX_PHASE_START, explosion_type);
6315 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6318 void SplashAcid(int x, int y)
6320 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6321 (!IN_LEV_FIELD(x - 1, y - 2) ||
6322 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6323 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6325 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6326 (!IN_LEV_FIELD(x + 1, y - 2) ||
6327 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6328 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6330 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6333 static void InitBeltMovement()
6335 static int belt_base_element[4] =
6337 EL_CONVEYOR_BELT_1_LEFT,
6338 EL_CONVEYOR_BELT_2_LEFT,
6339 EL_CONVEYOR_BELT_3_LEFT,
6340 EL_CONVEYOR_BELT_4_LEFT
6342 static int belt_base_active_element[4] =
6344 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6345 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6346 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6347 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6352 /* set frame order for belt animation graphic according to belt direction */
6353 for (i = 0; i < NUM_BELTS; i++)
6357 for (j = 0; j < NUM_BELT_PARTS; j++)
6359 int element = belt_base_active_element[belt_nr] + j;
6360 int graphic_1 = el2img(element);
6361 int graphic_2 = el2panelimg(element);
6363 if (game.belt_dir[i] == MV_LEFT)
6365 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6366 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6370 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6371 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6376 SCAN_PLAYFIELD(x, y)
6378 int element = Feld[x][y];
6380 for (i = 0; i < NUM_BELTS; i++)
6382 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6384 int e_belt_nr = getBeltNrFromBeltElement(element);
6387 if (e_belt_nr == belt_nr)
6389 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6391 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6398 static void ToggleBeltSwitch(int x, int y)
6400 static int belt_base_element[4] =
6402 EL_CONVEYOR_BELT_1_LEFT,
6403 EL_CONVEYOR_BELT_2_LEFT,
6404 EL_CONVEYOR_BELT_3_LEFT,
6405 EL_CONVEYOR_BELT_4_LEFT
6407 static int belt_base_active_element[4] =
6409 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6410 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6411 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6412 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6414 static int belt_base_switch_element[4] =
6416 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6417 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6418 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6419 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6421 static int belt_move_dir[4] =
6429 int element = Feld[x][y];
6430 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6431 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6432 int belt_dir = belt_move_dir[belt_dir_nr];
6435 if (!IS_BELT_SWITCH(element))
6438 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6439 game.belt_dir[belt_nr] = belt_dir;
6441 if (belt_dir_nr == 3)
6444 /* set frame order for belt animation graphic according to belt direction */
6445 for (i = 0; i < NUM_BELT_PARTS; i++)
6447 int element = belt_base_active_element[belt_nr] + i;
6448 int graphic_1 = el2img(element);
6449 int graphic_2 = el2panelimg(element);
6451 if (belt_dir == MV_LEFT)
6453 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6454 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6458 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6459 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6463 SCAN_PLAYFIELD(xx, yy)
6465 int element = Feld[xx][yy];
6467 if (IS_BELT_SWITCH(element))
6469 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6471 if (e_belt_nr == belt_nr)
6473 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6474 TEST_DrawLevelField(xx, yy);
6477 else if (IS_BELT(element) && belt_dir != MV_NONE)
6479 int e_belt_nr = getBeltNrFromBeltElement(element);
6481 if (e_belt_nr == belt_nr)
6483 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6485 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6486 TEST_DrawLevelField(xx, yy);
6489 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6491 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6493 if (e_belt_nr == belt_nr)
6495 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6497 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6498 TEST_DrawLevelField(xx, yy);
6504 static void ToggleSwitchgateSwitch(int x, int y)
6508 game.switchgate_pos = !game.switchgate_pos;
6510 SCAN_PLAYFIELD(xx, yy)
6512 int element = Feld[xx][yy];
6514 #if !USE_BOTH_SWITCHGATE_SWITCHES
6515 if (element == EL_SWITCHGATE_SWITCH_UP ||
6516 element == EL_SWITCHGATE_SWITCH_DOWN)
6518 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6519 TEST_DrawLevelField(xx, yy);
6521 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6522 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6524 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6525 TEST_DrawLevelField(xx, yy);
6528 if (element == EL_SWITCHGATE_SWITCH_UP)
6530 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6531 TEST_DrawLevelField(xx, yy);
6533 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6535 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6536 TEST_DrawLevelField(xx, yy);
6538 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6540 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6541 TEST_DrawLevelField(xx, yy);
6543 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6545 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6546 TEST_DrawLevelField(xx, yy);
6549 else if (element == EL_SWITCHGATE_OPEN ||
6550 element == EL_SWITCHGATE_OPENING)
6552 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6554 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6556 else if (element == EL_SWITCHGATE_CLOSED ||
6557 element == EL_SWITCHGATE_CLOSING)
6559 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6561 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6566 static int getInvisibleActiveFromInvisibleElement(int element)
6568 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6569 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6570 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6574 static int getInvisibleFromInvisibleActiveElement(int element)
6576 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6577 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6578 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6582 static void RedrawAllLightSwitchesAndInvisibleElements()
6586 SCAN_PLAYFIELD(x, y)
6588 int element = Feld[x][y];
6590 if (element == EL_LIGHT_SWITCH &&
6591 game.light_time_left > 0)
6593 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6594 TEST_DrawLevelField(x, y);
6596 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6597 game.light_time_left == 0)
6599 Feld[x][y] = EL_LIGHT_SWITCH;
6600 TEST_DrawLevelField(x, y);
6602 else if (element == EL_EMC_DRIPPER &&
6603 game.light_time_left > 0)
6605 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6606 TEST_DrawLevelField(x, y);
6608 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6609 game.light_time_left == 0)
6611 Feld[x][y] = EL_EMC_DRIPPER;
6612 TEST_DrawLevelField(x, y);
6614 else if (element == EL_INVISIBLE_STEELWALL ||
6615 element == EL_INVISIBLE_WALL ||
6616 element == EL_INVISIBLE_SAND)
6618 if (game.light_time_left > 0)
6619 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6621 TEST_DrawLevelField(x, y);
6623 /* uncrumble neighbour fields, if needed */
6624 if (element == EL_INVISIBLE_SAND)
6625 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6627 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6628 element == EL_INVISIBLE_WALL_ACTIVE ||
6629 element == EL_INVISIBLE_SAND_ACTIVE)
6631 if (game.light_time_left == 0)
6632 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6634 TEST_DrawLevelField(x, y);
6636 /* re-crumble neighbour fields, if needed */
6637 if (element == EL_INVISIBLE_SAND)
6638 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6643 static void RedrawAllInvisibleElementsForLenses()
6647 SCAN_PLAYFIELD(x, y)
6649 int element = Feld[x][y];
6651 if (element == EL_EMC_DRIPPER &&
6652 game.lenses_time_left > 0)
6654 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6655 TEST_DrawLevelField(x, y);
6657 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6658 game.lenses_time_left == 0)
6660 Feld[x][y] = EL_EMC_DRIPPER;
6661 TEST_DrawLevelField(x, y);
6663 else if (element == EL_INVISIBLE_STEELWALL ||
6664 element == EL_INVISIBLE_WALL ||
6665 element == EL_INVISIBLE_SAND)
6667 if (game.lenses_time_left > 0)
6668 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6670 TEST_DrawLevelField(x, y);
6672 /* uncrumble neighbour fields, if needed */
6673 if (element == EL_INVISIBLE_SAND)
6674 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6676 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6677 element == EL_INVISIBLE_WALL_ACTIVE ||
6678 element == EL_INVISIBLE_SAND_ACTIVE)
6680 if (game.lenses_time_left == 0)
6681 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6683 TEST_DrawLevelField(x, y);
6685 /* re-crumble neighbour fields, if needed */
6686 if (element == EL_INVISIBLE_SAND)
6687 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6692 static void RedrawAllInvisibleElementsForMagnifier()
6696 SCAN_PLAYFIELD(x, y)
6698 int element = Feld[x][y];
6700 if (element == EL_EMC_FAKE_GRASS &&
6701 game.magnify_time_left > 0)
6703 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6704 TEST_DrawLevelField(x, y);
6706 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6707 game.magnify_time_left == 0)
6709 Feld[x][y] = EL_EMC_FAKE_GRASS;
6710 TEST_DrawLevelField(x, y);
6712 else if (IS_GATE_GRAY(element) &&
6713 game.magnify_time_left > 0)
6715 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6716 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6717 IS_EM_GATE_GRAY(element) ?
6718 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6719 IS_EMC_GATE_GRAY(element) ?
6720 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6721 IS_DC_GATE_GRAY(element) ?
6722 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6724 TEST_DrawLevelField(x, y);
6726 else if (IS_GATE_GRAY_ACTIVE(element) &&
6727 game.magnify_time_left == 0)
6729 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6730 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6731 IS_EM_GATE_GRAY_ACTIVE(element) ?
6732 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6733 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6734 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6735 IS_DC_GATE_GRAY_ACTIVE(element) ?
6736 EL_DC_GATE_WHITE_GRAY :
6738 TEST_DrawLevelField(x, y);
6743 static void ToggleLightSwitch(int x, int y)
6745 int element = Feld[x][y];
6747 game.light_time_left =
6748 (element == EL_LIGHT_SWITCH ?
6749 level.time_light * FRAMES_PER_SECOND : 0);
6751 RedrawAllLightSwitchesAndInvisibleElements();
6754 static void ActivateTimegateSwitch(int x, int y)
6758 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6760 SCAN_PLAYFIELD(xx, yy)
6762 int element = Feld[xx][yy];
6764 if (element == EL_TIMEGATE_CLOSED ||
6765 element == EL_TIMEGATE_CLOSING)
6767 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6768 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6772 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6774 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6775 TEST_DrawLevelField(xx, yy);
6782 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6783 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6785 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6789 void Impact(int x, int y)
6791 boolean last_line = (y == lev_fieldy - 1);
6792 boolean object_hit = FALSE;
6793 boolean impact = (last_line || object_hit);
6794 int element = Feld[x][y];
6795 int smashed = EL_STEELWALL;
6797 if (!last_line) /* check if element below was hit */
6799 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6802 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6803 MovDir[x][y + 1] != MV_DOWN ||
6804 MovPos[x][y + 1] <= TILEY / 2));
6806 /* do not smash moving elements that left the smashed field in time */
6807 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6808 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6811 #if USE_QUICKSAND_IMPACT_BUGFIX
6812 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6814 RemoveMovingField(x, y + 1);
6815 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6816 Feld[x][y + 2] = EL_ROCK;
6817 TEST_DrawLevelField(x, y + 2);
6822 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6824 RemoveMovingField(x, y + 1);
6825 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6826 Feld[x][y + 2] = EL_ROCK;
6827 TEST_DrawLevelField(x, y + 2);
6834 smashed = MovingOrBlocked2Element(x, y + 1);
6836 impact = (last_line || object_hit);
6839 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6841 SplashAcid(x, y + 1);
6845 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6846 /* only reset graphic animation if graphic really changes after impact */
6848 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6850 ResetGfxAnimation(x, y);
6851 TEST_DrawLevelField(x, y);
6854 if (impact && CAN_EXPLODE_IMPACT(element))
6859 else if (impact && element == EL_PEARL &&
6860 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6862 ResetGfxAnimation(x, y);
6864 Feld[x][y] = EL_PEARL_BREAKING;
6865 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6868 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6870 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6875 if (impact && element == EL_AMOEBA_DROP)
6877 if (object_hit && IS_PLAYER(x, y + 1))
6878 KillPlayerUnlessEnemyProtected(x, y + 1);
6879 else if (object_hit && smashed == EL_PENGUIN)
6883 Feld[x][y] = EL_AMOEBA_GROWING;
6884 Store[x][y] = EL_AMOEBA_WET;
6886 ResetRandomAnimationValue(x, y);
6891 if (object_hit) /* check which object was hit */
6893 if ((CAN_PASS_MAGIC_WALL(element) &&
6894 (smashed == EL_MAGIC_WALL ||
6895 smashed == EL_BD_MAGIC_WALL)) ||
6896 (CAN_PASS_DC_MAGIC_WALL(element) &&
6897 smashed == EL_DC_MAGIC_WALL))
6900 int activated_magic_wall =
6901 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6902 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6903 EL_DC_MAGIC_WALL_ACTIVE);
6905 /* activate magic wall / mill */
6906 SCAN_PLAYFIELD(xx, yy)
6908 if (Feld[xx][yy] == smashed)
6909 Feld[xx][yy] = activated_magic_wall;
6912 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6913 game.magic_wall_active = TRUE;
6915 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6916 SND_MAGIC_WALL_ACTIVATING :
6917 smashed == EL_BD_MAGIC_WALL ?
6918 SND_BD_MAGIC_WALL_ACTIVATING :
6919 SND_DC_MAGIC_WALL_ACTIVATING));
6922 if (IS_PLAYER(x, y + 1))
6924 if (CAN_SMASH_PLAYER(element))
6926 KillPlayerUnlessEnemyProtected(x, y + 1);
6930 else if (smashed == EL_PENGUIN)
6932 if (CAN_SMASH_PLAYER(element))
6938 else if (element == EL_BD_DIAMOND)
6940 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6946 else if (((element == EL_SP_INFOTRON ||
6947 element == EL_SP_ZONK) &&
6948 (smashed == EL_SP_SNIKSNAK ||
6949 smashed == EL_SP_ELECTRON ||
6950 smashed == EL_SP_DISK_ORANGE)) ||
6951 (element == EL_SP_INFOTRON &&
6952 smashed == EL_SP_DISK_YELLOW))
6957 else if (CAN_SMASH_EVERYTHING(element))
6959 if (IS_CLASSIC_ENEMY(smashed) ||
6960 CAN_EXPLODE_SMASHED(smashed))
6965 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6967 if (smashed == EL_LAMP ||
6968 smashed == EL_LAMP_ACTIVE)
6973 else if (smashed == EL_NUT)
6975 Feld[x][y + 1] = EL_NUT_BREAKING;
6976 PlayLevelSound(x, y, SND_NUT_BREAKING);
6977 RaiseScoreElement(EL_NUT);
6980 else if (smashed == EL_PEARL)
6982 ResetGfxAnimation(x, y);
6984 Feld[x][y + 1] = EL_PEARL_BREAKING;
6985 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6988 else if (smashed == EL_DIAMOND)
6990 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6991 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6994 else if (IS_BELT_SWITCH(smashed))
6996 ToggleBeltSwitch(x, y + 1);
6998 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6999 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7000 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7001 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7003 ToggleSwitchgateSwitch(x, y + 1);
7005 else if (smashed == EL_LIGHT_SWITCH ||
7006 smashed == EL_LIGHT_SWITCH_ACTIVE)
7008 ToggleLightSwitch(x, y + 1);
7013 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7016 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7018 CheckElementChangeBySide(x, y + 1, smashed, element,
7019 CE_SWITCHED, CH_SIDE_TOP);
7020 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7026 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7031 /* play sound of magic wall / mill */
7033 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7034 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7035 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7037 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7038 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7039 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7040 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7041 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7042 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7047 /* play sound of object that hits the ground */
7048 if (last_line || object_hit)
7049 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7052 inline static void TurnRoundExt(int x, int y)
7064 { 0, 0 }, { 0, 0 }, { 0, 0 },
7069 int left, right, back;
7073 { MV_DOWN, MV_UP, MV_RIGHT },
7074 { MV_UP, MV_DOWN, MV_LEFT },
7076 { MV_LEFT, MV_RIGHT, MV_DOWN },
7080 { MV_RIGHT, MV_LEFT, MV_UP }
7083 int element = Feld[x][y];
7084 int move_pattern = element_info[element].move_pattern;
7086 int old_move_dir = MovDir[x][y];
7087 int left_dir = turn[old_move_dir].left;
7088 int right_dir = turn[old_move_dir].right;
7089 int back_dir = turn[old_move_dir].back;
7091 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7092 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7093 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7094 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7096 int left_x = x + left_dx, left_y = y + left_dy;
7097 int right_x = x + right_dx, right_y = y + right_dy;
7098 int move_x = x + move_dx, move_y = y + move_dy;
7102 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7104 TestIfBadThingTouchesOtherBadThing(x, y);
7106 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7107 MovDir[x][y] = right_dir;
7108 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7109 MovDir[x][y] = left_dir;
7111 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7113 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7116 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7118 TestIfBadThingTouchesOtherBadThing(x, y);
7120 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7121 MovDir[x][y] = left_dir;
7122 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7123 MovDir[x][y] = right_dir;
7125 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7127 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7130 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7132 TestIfBadThingTouchesOtherBadThing(x, y);
7134 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7135 MovDir[x][y] = left_dir;
7136 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7137 MovDir[x][y] = right_dir;
7139 if (MovDir[x][y] != old_move_dir)
7142 else if (element == EL_YAMYAM)
7144 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7145 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7147 if (can_turn_left && can_turn_right)
7148 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149 else if (can_turn_left)
7150 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151 else if (can_turn_right)
7152 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7154 MovDir[x][y] = back_dir;
7156 MovDelay[x][y] = 16 + 16 * RND(3);
7158 else if (element == EL_DARK_YAMYAM)
7160 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7162 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7165 if (can_turn_left && can_turn_right)
7166 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7167 else if (can_turn_left)
7168 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7169 else if (can_turn_right)
7170 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7172 MovDir[x][y] = back_dir;
7174 MovDelay[x][y] = 16 + 16 * RND(3);
7176 else if (element == EL_PACMAN)
7178 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7179 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7181 if (can_turn_left && can_turn_right)
7182 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7183 else if (can_turn_left)
7184 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7185 else if (can_turn_right)
7186 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7188 MovDir[x][y] = back_dir;
7190 MovDelay[x][y] = 6 + RND(40);
7192 else if (element == EL_PIG)
7194 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7195 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7196 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7197 boolean should_turn_left, should_turn_right, should_move_on;
7199 int rnd = RND(rnd_value);
7201 should_turn_left = (can_turn_left &&
7203 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7204 y + back_dy + left_dy)));
7205 should_turn_right = (can_turn_right &&
7207 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7208 y + back_dy + right_dy)));
7209 should_move_on = (can_move_on &&
7212 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7213 y + move_dy + left_dy) ||
7214 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7215 y + move_dy + right_dy)));
7217 if (should_turn_left || should_turn_right || should_move_on)
7219 if (should_turn_left && should_turn_right && should_move_on)
7220 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7221 rnd < 2 * rnd_value / 3 ? right_dir :
7223 else if (should_turn_left && should_turn_right)
7224 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7225 else if (should_turn_left && should_move_on)
7226 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7227 else if (should_turn_right && should_move_on)
7228 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7229 else if (should_turn_left)
7230 MovDir[x][y] = left_dir;
7231 else if (should_turn_right)
7232 MovDir[x][y] = right_dir;
7233 else if (should_move_on)
7234 MovDir[x][y] = old_move_dir;
7236 else if (can_move_on && rnd > rnd_value / 8)
7237 MovDir[x][y] = old_move_dir;
7238 else if (can_turn_left && can_turn_right)
7239 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7240 else if (can_turn_left && rnd > rnd_value / 8)
7241 MovDir[x][y] = left_dir;
7242 else if (can_turn_right && rnd > rnd_value/8)
7243 MovDir[x][y] = right_dir;
7245 MovDir[x][y] = back_dir;
7247 xx = x + move_xy[MovDir[x][y]].dx;
7248 yy = y + move_xy[MovDir[x][y]].dy;
7250 if (!IN_LEV_FIELD(xx, yy) ||
7251 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7252 MovDir[x][y] = old_move_dir;
7256 else if (element == EL_DRAGON)
7258 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7259 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7260 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7262 int rnd = RND(rnd_value);
7264 if (can_move_on && rnd > rnd_value / 8)
7265 MovDir[x][y] = old_move_dir;
7266 else if (can_turn_left && can_turn_right)
7267 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7268 else if (can_turn_left && rnd > rnd_value / 8)
7269 MovDir[x][y] = left_dir;
7270 else if (can_turn_right && rnd > rnd_value / 8)
7271 MovDir[x][y] = right_dir;
7273 MovDir[x][y] = back_dir;
7275 xx = x + move_xy[MovDir[x][y]].dx;
7276 yy = y + move_xy[MovDir[x][y]].dy;
7278 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7279 MovDir[x][y] = old_move_dir;
7283 else if (element == EL_MOLE)
7285 boolean can_move_on =
7286 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7287 IS_AMOEBOID(Feld[move_x][move_y]) ||
7288 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7291 boolean can_turn_left =
7292 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7293 IS_AMOEBOID(Feld[left_x][left_y])));
7295 boolean can_turn_right =
7296 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7297 IS_AMOEBOID(Feld[right_x][right_y])));
7299 if (can_turn_left && can_turn_right)
7300 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7301 else if (can_turn_left)
7302 MovDir[x][y] = left_dir;
7304 MovDir[x][y] = right_dir;
7307 if (MovDir[x][y] != old_move_dir)
7310 else if (element == EL_BALLOON)
7312 MovDir[x][y] = game.wind_direction;
7315 else if (element == EL_SPRING)
7317 #if USE_NEW_SPRING_BUMPER
7318 if (MovDir[x][y] & MV_HORIZONTAL)
7320 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7321 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7323 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7324 ResetGfxAnimation(move_x, move_y);
7325 TEST_DrawLevelField(move_x, move_y);
7327 MovDir[x][y] = back_dir;
7329 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7330 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7331 MovDir[x][y] = MV_NONE;
7334 if (MovDir[x][y] & MV_HORIZONTAL &&
7335 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7336 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7337 MovDir[x][y] = MV_NONE;
7342 else if (element == EL_ROBOT ||
7343 element == EL_SATELLITE ||
7344 element == EL_PENGUIN ||
7345 element == EL_EMC_ANDROID)
7347 int attr_x = -1, attr_y = -1;
7358 for (i = 0; i < MAX_PLAYERS; i++)
7360 struct PlayerInfo *player = &stored_player[i];
7361 int jx = player->jx, jy = player->jy;
7363 if (!player->active)
7367 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7375 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7376 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7377 game.engine_version < VERSION_IDENT(3,1,0,0)))
7383 if (element == EL_PENGUIN)
7386 static int xy[4][2] =
7394 for (i = 0; i < NUM_DIRECTIONS; i++)
7396 int ex = x + xy[i][0];
7397 int ey = y + xy[i][1];
7399 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7400 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7401 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7402 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7411 MovDir[x][y] = MV_NONE;
7413 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7414 else if (attr_x > x)
7415 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7417 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7418 else if (attr_y > y)
7419 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7421 if (element == EL_ROBOT)
7425 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7426 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7427 Moving2Blocked(x, y, &newx, &newy);
7429 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7430 MovDelay[x][y] = 8 + 8 * !RND(3);
7432 MovDelay[x][y] = 16;
7434 else if (element == EL_PENGUIN)
7440 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7442 boolean first_horiz = RND(2);
7443 int new_move_dir = MovDir[x][y];
7446 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7447 Moving2Blocked(x, y, &newx, &newy);
7449 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7453 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7454 Moving2Blocked(x, y, &newx, &newy);
7456 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7459 MovDir[x][y] = old_move_dir;
7463 else if (element == EL_SATELLITE)
7469 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7471 boolean first_horiz = RND(2);
7472 int new_move_dir = MovDir[x][y];
7475 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7476 Moving2Blocked(x, y, &newx, &newy);
7478 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7482 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7483 Moving2Blocked(x, y, &newx, &newy);
7485 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7488 MovDir[x][y] = old_move_dir;
7492 else if (element == EL_EMC_ANDROID)
7494 static int check_pos[16] =
7496 -1, /* 0 => (invalid) */
7497 7, /* 1 => MV_LEFT */
7498 3, /* 2 => MV_RIGHT */
7499 -1, /* 3 => (invalid) */
7501 0, /* 5 => MV_LEFT | MV_UP */
7502 2, /* 6 => MV_RIGHT | MV_UP */
7503 -1, /* 7 => (invalid) */
7504 5, /* 8 => MV_DOWN */
7505 6, /* 9 => MV_LEFT | MV_DOWN */
7506 4, /* 10 => MV_RIGHT | MV_DOWN */
7507 -1, /* 11 => (invalid) */
7508 -1, /* 12 => (invalid) */
7509 -1, /* 13 => (invalid) */
7510 -1, /* 14 => (invalid) */
7511 -1, /* 15 => (invalid) */
7519 { -1, -1, MV_LEFT | MV_UP },
7521 { +1, -1, MV_RIGHT | MV_UP },
7522 { +1, 0, MV_RIGHT },
7523 { +1, +1, MV_RIGHT | MV_DOWN },
7525 { -1, +1, MV_LEFT | MV_DOWN },
7528 int start_pos, check_order;
7529 boolean can_clone = FALSE;
7532 /* check if there is any free field around current position */
7533 for (i = 0; i < 8; i++)
7535 int newx = x + check_xy[i].dx;
7536 int newy = y + check_xy[i].dy;
7538 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7546 if (can_clone) /* randomly find an element to clone */
7550 start_pos = check_pos[RND(8)];
7551 check_order = (RND(2) ? -1 : +1);
7553 for (i = 0; i < 8; i++)
7555 int pos_raw = start_pos + i * check_order;
7556 int pos = (pos_raw + 8) % 8;
7557 int newx = x + check_xy[pos].dx;
7558 int newy = y + check_xy[pos].dy;
7560 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7562 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7563 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7565 Store[x][y] = Feld[newx][newy];
7574 if (can_clone) /* randomly find a direction to move */
7578 start_pos = check_pos[RND(8)];
7579 check_order = (RND(2) ? -1 : +1);
7581 for (i = 0; i < 8; i++)
7583 int pos_raw = start_pos + i * check_order;
7584 int pos = (pos_raw + 8) % 8;
7585 int newx = x + check_xy[pos].dx;
7586 int newy = y + check_xy[pos].dy;
7587 int new_move_dir = check_xy[pos].dir;
7589 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7591 MovDir[x][y] = new_move_dir;
7592 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7601 if (can_clone) /* cloning and moving successful */
7604 /* cannot clone -- try to move towards player */
7606 start_pos = check_pos[MovDir[x][y] & 0x0f];
7607 check_order = (RND(2) ? -1 : +1);
7609 for (i = 0; i < 3; i++)
7611 /* first check start_pos, then previous/next or (next/previous) pos */
7612 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7613 int pos = (pos_raw + 8) % 8;
7614 int newx = x + check_xy[pos].dx;
7615 int newy = y + check_xy[pos].dy;
7616 int new_move_dir = check_xy[pos].dir;
7618 if (IS_PLAYER(newx, newy))
7621 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7623 MovDir[x][y] = new_move_dir;
7624 MovDelay[x][y] = level.android_move_time * 8 + 1;
7631 else if (move_pattern == MV_TURNING_LEFT ||
7632 move_pattern == MV_TURNING_RIGHT ||
7633 move_pattern == MV_TURNING_LEFT_RIGHT ||
7634 move_pattern == MV_TURNING_RIGHT_LEFT ||
7635 move_pattern == MV_TURNING_RANDOM ||
7636 move_pattern == MV_ALL_DIRECTIONS)
7638 boolean can_turn_left =
7639 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7640 boolean can_turn_right =
7641 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7643 if (element_info[element].move_stepsize == 0) /* "not moving" */
7646 if (move_pattern == MV_TURNING_LEFT)
7647 MovDir[x][y] = left_dir;
7648 else if (move_pattern == MV_TURNING_RIGHT)
7649 MovDir[x][y] = right_dir;
7650 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7651 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7652 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7653 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7654 else if (move_pattern == MV_TURNING_RANDOM)
7655 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7656 can_turn_right && !can_turn_left ? right_dir :
7657 RND(2) ? left_dir : right_dir);
7658 else if (can_turn_left && can_turn_right)
7659 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7660 else if (can_turn_left)
7661 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7662 else if (can_turn_right)
7663 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7665 MovDir[x][y] = back_dir;
7667 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7669 else if (move_pattern == MV_HORIZONTAL ||
7670 move_pattern == MV_VERTICAL)
7672 if (move_pattern & old_move_dir)
7673 MovDir[x][y] = back_dir;
7674 else if (move_pattern == MV_HORIZONTAL)
7675 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7676 else if (move_pattern == MV_VERTICAL)
7677 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7679 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7681 else if (move_pattern & MV_ANY_DIRECTION)
7683 MovDir[x][y] = move_pattern;
7684 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7686 else if (move_pattern & MV_WIND_DIRECTION)
7688 MovDir[x][y] = game.wind_direction;
7689 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7691 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7693 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7694 MovDir[x][y] = left_dir;
7695 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7696 MovDir[x][y] = right_dir;
7698 if (MovDir[x][y] != old_move_dir)
7699 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7701 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7703 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7704 MovDir[x][y] = right_dir;
7705 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7706 MovDir[x][y] = left_dir;
7708 if (MovDir[x][y] != old_move_dir)
7709 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7711 else if (move_pattern == MV_TOWARDS_PLAYER ||
7712 move_pattern == MV_AWAY_FROM_PLAYER)
7714 int attr_x = -1, attr_y = -1;
7716 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7727 for (i = 0; i < MAX_PLAYERS; i++)
7729 struct PlayerInfo *player = &stored_player[i];
7730 int jx = player->jx, jy = player->jy;
7732 if (!player->active)
7736 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7744 MovDir[x][y] = MV_NONE;
7746 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7747 else if (attr_x > x)
7748 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7750 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7751 else if (attr_y > y)
7752 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7754 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7756 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7758 boolean first_horiz = RND(2);
7759 int new_move_dir = MovDir[x][y];
7761 if (element_info[element].move_stepsize == 0) /* "not moving" */
7763 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7764 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7770 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7771 Moving2Blocked(x, y, &newx, &newy);
7773 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7777 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7778 Moving2Blocked(x, y, &newx, &newy);
7780 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7783 MovDir[x][y] = old_move_dir;
7786 else if (move_pattern == MV_WHEN_PUSHED ||
7787 move_pattern == MV_WHEN_DROPPED)
7789 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7790 MovDir[x][y] = MV_NONE;
7794 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7796 static int test_xy[7][2] =
7806 static int test_dir[7] =
7816 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7817 int move_preference = -1000000; /* start with very low preference */
7818 int new_move_dir = MV_NONE;
7819 int start_test = RND(4);
7822 for (i = 0; i < NUM_DIRECTIONS; i++)
7824 int move_dir = test_dir[start_test + i];
7825 int move_dir_preference;
7827 xx = x + test_xy[start_test + i][0];
7828 yy = y + test_xy[start_test + i][1];
7830 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7831 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7833 new_move_dir = move_dir;
7838 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7841 move_dir_preference = -1 * RunnerVisit[xx][yy];
7842 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7843 move_dir_preference = PlayerVisit[xx][yy];
7845 if (move_dir_preference > move_preference)
7847 /* prefer field that has not been visited for the longest time */
7848 move_preference = move_dir_preference;
7849 new_move_dir = move_dir;
7851 else if (move_dir_preference == move_preference &&
7852 move_dir == old_move_dir)
7854 /* prefer last direction when all directions are preferred equally */
7855 move_preference = move_dir_preference;
7856 new_move_dir = move_dir;
7860 MovDir[x][y] = new_move_dir;
7861 if (old_move_dir != new_move_dir)
7862 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7866 static void TurnRound(int x, int y)
7868 int direction = MovDir[x][y];
7872 GfxDir[x][y] = MovDir[x][y];
7874 if (direction != MovDir[x][y])
7878 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7880 ResetGfxFrame(x, y, FALSE);
7883 static boolean JustBeingPushed(int x, int y)
7887 for (i = 0; i < MAX_PLAYERS; i++)
7889 struct PlayerInfo *player = &stored_player[i];
7891 if (player->active && player->is_pushing && player->MovPos)
7893 int next_jx = player->jx + (player->jx - player->last_jx);
7894 int next_jy = player->jy + (player->jy - player->last_jy);
7896 if (x == next_jx && y == next_jy)
7904 void StartMoving(int x, int y)
7906 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7907 int element = Feld[x][y];
7912 if (MovDelay[x][y] == 0)
7913 GfxAction[x][y] = ACTION_DEFAULT;
7915 if (CAN_FALL(element) && y < lev_fieldy - 1)
7917 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7918 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7919 if (JustBeingPushed(x, y))
7922 if (element == EL_QUICKSAND_FULL)
7924 if (IS_FREE(x, y + 1))
7926 InitMovingField(x, y, MV_DOWN);
7927 started_moving = TRUE;
7929 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7930 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7931 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7932 Store[x][y] = EL_ROCK;
7934 Store[x][y] = EL_ROCK;
7937 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7939 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7941 if (!MovDelay[x][y])
7943 MovDelay[x][y] = TILEY + 1;
7945 ResetGfxAnimation(x, y);
7946 ResetGfxAnimation(x, y + 1);
7951 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7959 Feld[x][y] = EL_QUICKSAND_EMPTY;
7960 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7961 Store[x][y + 1] = Store[x][y];
7964 PlayLevelSoundAction(x, y, ACTION_FILLING);
7966 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7968 if (!MovDelay[x][y])
7970 MovDelay[x][y] = TILEY + 1;
7972 ResetGfxAnimation(x, y);
7973 ResetGfxAnimation(x, y + 1);
7978 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7979 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7986 Feld[x][y] = EL_QUICKSAND_EMPTY;
7987 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7988 Store[x][y + 1] = Store[x][y];
7991 PlayLevelSoundAction(x, y, ACTION_FILLING);
7994 else if (element == EL_QUICKSAND_FAST_FULL)
7996 if (IS_FREE(x, y + 1))
7998 InitMovingField(x, y, MV_DOWN);
7999 started_moving = TRUE;
8001 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8002 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8003 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8004 Store[x][y] = EL_ROCK;
8006 Store[x][y] = EL_ROCK;
8009 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8011 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8013 if (!MovDelay[x][y])
8015 MovDelay[x][y] = TILEY + 1;
8017 ResetGfxAnimation(x, y);
8018 ResetGfxAnimation(x, y + 1);
8023 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8031 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8033 Store[x][y + 1] = Store[x][y];
8036 PlayLevelSoundAction(x, y, ACTION_FILLING);
8038 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8040 if (!MovDelay[x][y])
8042 MovDelay[x][y] = TILEY + 1;
8044 ResetGfxAnimation(x, y);
8045 ResetGfxAnimation(x, y + 1);
8050 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8051 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8058 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8059 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8060 Store[x][y + 1] = Store[x][y];
8063 PlayLevelSoundAction(x, y, ACTION_FILLING);
8066 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8067 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8069 InitMovingField(x, y, MV_DOWN);
8070 started_moving = TRUE;
8072 Feld[x][y] = EL_QUICKSAND_FILLING;
8073 Store[x][y] = element;
8075 PlayLevelSoundAction(x, y, ACTION_FILLING);
8077 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8078 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8080 InitMovingField(x, y, MV_DOWN);
8081 started_moving = TRUE;
8083 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8084 Store[x][y] = element;
8086 PlayLevelSoundAction(x, y, ACTION_FILLING);
8088 else if (element == EL_MAGIC_WALL_FULL)
8090 if (IS_FREE(x, y + 1))
8092 InitMovingField(x, y, MV_DOWN);
8093 started_moving = TRUE;
8095 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8096 Store[x][y] = EL_CHANGED(Store[x][y]);
8098 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8100 if (!MovDelay[x][y])
8101 MovDelay[x][y] = TILEY/4 + 1;
8110 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8111 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8112 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8116 else if (element == EL_BD_MAGIC_WALL_FULL)
8118 if (IS_FREE(x, y + 1))
8120 InitMovingField(x, y, MV_DOWN);
8121 started_moving = TRUE;
8123 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8124 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8126 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8128 if (!MovDelay[x][y])
8129 MovDelay[x][y] = TILEY/4 + 1;
8138 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8139 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8140 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8144 else if (element == EL_DC_MAGIC_WALL_FULL)
8146 if (IS_FREE(x, y + 1))
8148 InitMovingField(x, y, MV_DOWN);
8149 started_moving = TRUE;
8151 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8152 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8154 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8156 if (!MovDelay[x][y])
8157 MovDelay[x][y] = TILEY/4 + 1;
8166 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8167 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8168 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8172 else if ((CAN_PASS_MAGIC_WALL(element) &&
8173 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8174 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8175 (CAN_PASS_DC_MAGIC_WALL(element) &&
8176 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8179 InitMovingField(x, y, MV_DOWN);
8180 started_moving = TRUE;
8183 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8184 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8185 EL_DC_MAGIC_WALL_FILLING);
8186 Store[x][y] = element;
8188 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8190 SplashAcid(x, y + 1);
8192 InitMovingField(x, y, MV_DOWN);
8193 started_moving = TRUE;
8195 Store[x][y] = EL_ACID;
8198 #if USE_FIX_IMPACT_COLLISION
8199 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8200 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8202 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8203 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8205 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8206 CAN_FALL(element) && WasJustFalling[x][y] &&
8207 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8209 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8210 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8211 (Feld[x][y + 1] == EL_BLOCKED)))
8213 /* this is needed for a special case not covered by calling "Impact()"
8214 from "ContinueMoving()": if an element moves to a tile directly below
8215 another element which was just falling on that tile (which was empty
8216 in the previous frame), the falling element above would just stop
8217 instead of smashing the element below (in previous version, the above
8218 element was just checked for "moving" instead of "falling", resulting
8219 in incorrect smashes caused by horizontal movement of the above
8220 element; also, the case of the player being the element to smash was
8221 simply not covered here... :-/ ) */
8223 CheckCollision[x][y] = 0;
8224 CheckImpact[x][y] = 0;
8228 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8230 if (MovDir[x][y] == MV_NONE)
8232 InitMovingField(x, y, MV_DOWN);
8233 started_moving = TRUE;
8236 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8238 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8239 MovDir[x][y] = MV_DOWN;
8241 InitMovingField(x, y, MV_DOWN);
8242 started_moving = TRUE;
8244 else if (element == EL_AMOEBA_DROP)
8246 Feld[x][y] = EL_AMOEBA_GROWING;
8247 Store[x][y] = EL_AMOEBA_WET;
8249 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8250 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8251 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8252 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8254 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8255 (IS_FREE(x - 1, y + 1) ||
8256 Feld[x - 1][y + 1] == EL_ACID));
8257 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8258 (IS_FREE(x + 1, y + 1) ||
8259 Feld[x + 1][y + 1] == EL_ACID));
8260 boolean can_fall_any = (can_fall_left || can_fall_right);
8261 boolean can_fall_both = (can_fall_left && can_fall_right);
8262 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8264 #if USE_NEW_ALL_SLIPPERY
8265 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8267 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8268 can_fall_right = FALSE;
8269 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8270 can_fall_left = FALSE;
8271 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8272 can_fall_right = FALSE;
8273 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8274 can_fall_left = FALSE;
8276 can_fall_any = (can_fall_left || can_fall_right);
8277 can_fall_both = FALSE;
8280 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8282 if (slippery_type == SLIPPERY_ONLY_LEFT)
8283 can_fall_right = FALSE;
8284 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8285 can_fall_left = FALSE;
8286 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8287 can_fall_right = FALSE;
8288 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8289 can_fall_left = FALSE;
8291 can_fall_any = (can_fall_left || can_fall_right);
8292 can_fall_both = (can_fall_left && can_fall_right);
8296 #if USE_NEW_ALL_SLIPPERY
8298 #if USE_NEW_SP_SLIPPERY
8299 /* !!! better use the same properties as for custom elements here !!! */
8300 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8301 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8303 can_fall_right = FALSE; /* slip down on left side */
8304 can_fall_both = FALSE;
8309 #if USE_NEW_ALL_SLIPPERY
8312 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8313 can_fall_right = FALSE; /* slip down on left side */
8315 can_fall_left = !(can_fall_right = RND(2));
8317 can_fall_both = FALSE;
8322 if (game.emulation == EMU_BOULDERDASH ||
8323 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8324 can_fall_right = FALSE; /* slip down on left side */
8326 can_fall_left = !(can_fall_right = RND(2));
8328 can_fall_both = FALSE;
8334 /* if not determined otherwise, prefer left side for slipping down */
8335 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8336 started_moving = TRUE;
8340 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8342 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8345 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8346 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8347 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8348 int belt_dir = game.belt_dir[belt_nr];
8350 if ((belt_dir == MV_LEFT && left_is_free) ||
8351 (belt_dir == MV_RIGHT && right_is_free))
8353 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8355 InitMovingField(x, y, belt_dir);
8356 started_moving = TRUE;
8358 Pushed[x][y] = TRUE;
8359 Pushed[nextx][y] = TRUE;
8361 GfxAction[x][y] = ACTION_DEFAULT;
8365 MovDir[x][y] = 0; /* if element was moving, stop it */
8370 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8372 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8374 if (CAN_MOVE(element) && !started_moving)
8377 int move_pattern = element_info[element].move_pattern;
8382 if (MovDir[x][y] == MV_NONE)
8384 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8385 x, y, element, element_info[element].token_name);
8386 printf("StartMoving(): This should never happen!\n");
8391 Moving2Blocked(x, y, &newx, &newy);
8393 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8396 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8397 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8399 WasJustMoving[x][y] = 0;
8400 CheckCollision[x][y] = 0;
8402 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8404 if (Feld[x][y] != element) /* element has changed */
8408 if (!MovDelay[x][y]) /* start new movement phase */
8410 /* all objects that can change their move direction after each step
8411 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8413 if (element != EL_YAMYAM &&
8414 element != EL_DARK_YAMYAM &&
8415 element != EL_PACMAN &&
8416 !(move_pattern & MV_ANY_DIRECTION) &&
8417 move_pattern != MV_TURNING_LEFT &&
8418 move_pattern != MV_TURNING_RIGHT &&
8419 move_pattern != MV_TURNING_LEFT_RIGHT &&
8420 move_pattern != MV_TURNING_RIGHT_LEFT &&
8421 move_pattern != MV_TURNING_RANDOM)
8425 if (MovDelay[x][y] && (element == EL_BUG ||
8426 element == EL_SPACESHIP ||
8427 element == EL_SP_SNIKSNAK ||
8428 element == EL_SP_ELECTRON ||
8429 element == EL_MOLE))
8430 TEST_DrawLevelField(x, y);
8434 if (MovDelay[x][y]) /* wait some time before next movement */
8438 if (element == EL_ROBOT ||
8439 element == EL_YAMYAM ||
8440 element == EL_DARK_YAMYAM)
8442 DrawLevelElementAnimationIfNeeded(x, y, element);
8443 PlayLevelSoundAction(x, y, ACTION_WAITING);
8445 else if (element == EL_SP_ELECTRON)
8446 DrawLevelElementAnimationIfNeeded(x, y, element);
8447 else if (element == EL_DRAGON)
8450 int dir = MovDir[x][y];
8451 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8452 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8453 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8454 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8455 dir == MV_UP ? IMG_FLAMES_1_UP :
8456 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8457 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8459 GfxAction[x][y] = ACTION_ATTACKING;
8461 if (IS_PLAYER(x, y))
8462 DrawPlayerField(x, y);
8464 TEST_DrawLevelField(x, y);
8466 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8468 for (i = 1; i <= 3; i++)
8470 int xx = x + i * dx;
8471 int yy = y + i * dy;
8472 int sx = SCREENX(xx);
8473 int sy = SCREENY(yy);
8474 int flame_graphic = graphic + (i - 1);
8476 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8481 int flamed = MovingOrBlocked2Element(xx, yy);
8485 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8487 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8488 RemoveMovingField(xx, yy);
8490 RemoveField(xx, yy);
8492 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8495 RemoveMovingField(xx, yy);
8498 ChangeDelay[xx][yy] = 0;
8500 Feld[xx][yy] = EL_FLAMES;
8502 if (IN_SCR_FIELD(sx, sy))
8504 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8505 DrawGraphic(sx, sy, flame_graphic, frame);
8510 if (Feld[xx][yy] == EL_FLAMES)
8511 Feld[xx][yy] = EL_EMPTY;
8512 TEST_DrawLevelField(xx, yy);
8517 if (MovDelay[x][y]) /* element still has to wait some time */
8519 PlayLevelSoundAction(x, y, ACTION_WAITING);
8525 /* now make next step */
8527 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8529 if (DONT_COLLIDE_WITH(element) &&
8530 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8531 !PLAYER_ENEMY_PROTECTED(newx, newy))
8533 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8538 else if (CAN_MOVE_INTO_ACID(element) &&
8539 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8540 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8541 (MovDir[x][y] == MV_DOWN ||
8542 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8544 SplashAcid(newx, newy);
8545 Store[x][y] = EL_ACID;
8547 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8549 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8550 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8551 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8552 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8555 TEST_DrawLevelField(x, y);
8557 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8558 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8559 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8561 local_player->friends_still_needed--;
8562 if (!local_player->friends_still_needed &&
8563 !local_player->GameOver && AllPlayersGone)
8564 PlayerWins(local_player);
8568 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8570 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8571 TEST_DrawLevelField(newx, newy);
8573 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8575 else if (!IS_FREE(newx, newy))
8577 GfxAction[x][y] = ACTION_WAITING;
8579 if (IS_PLAYER(x, y))
8580 DrawPlayerField(x, y);
8582 TEST_DrawLevelField(x, y);
8587 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8589 if (IS_FOOD_PIG(Feld[newx][newy]))
8591 if (IS_MOVING(newx, newy))
8592 RemoveMovingField(newx, newy);
8595 Feld[newx][newy] = EL_EMPTY;
8596 TEST_DrawLevelField(newx, newy);
8599 PlayLevelSound(x, y, SND_PIG_DIGGING);
8601 else if (!IS_FREE(newx, newy))
8603 if (IS_PLAYER(x, y))
8604 DrawPlayerField(x, y);
8606 TEST_DrawLevelField(x, y);
8611 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8613 if (Store[x][y] != EL_EMPTY)
8615 boolean can_clone = FALSE;
8618 /* check if element to clone is still there */
8619 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8621 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8629 /* cannot clone or target field not free anymore -- do not clone */
8630 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8631 Store[x][y] = EL_EMPTY;
8634 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8636 if (IS_MV_DIAGONAL(MovDir[x][y]))
8638 int diagonal_move_dir = MovDir[x][y];
8639 int stored = Store[x][y];
8640 int change_delay = 8;
8643 /* android is moving diagonally */
8645 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8647 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8648 GfxElement[x][y] = EL_EMC_ANDROID;
8649 GfxAction[x][y] = ACTION_SHRINKING;
8650 GfxDir[x][y] = diagonal_move_dir;
8651 ChangeDelay[x][y] = change_delay;
8653 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8656 DrawLevelGraphicAnimation(x, y, graphic);
8657 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8659 if (Feld[newx][newy] == EL_ACID)
8661 SplashAcid(newx, newy);
8666 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8668 Store[newx][newy] = EL_EMC_ANDROID;
8669 GfxElement[newx][newy] = EL_EMC_ANDROID;
8670 GfxAction[newx][newy] = ACTION_GROWING;
8671 GfxDir[newx][newy] = diagonal_move_dir;
8672 ChangeDelay[newx][newy] = change_delay;
8674 graphic = el_act_dir2img(GfxElement[newx][newy],
8675 GfxAction[newx][newy], GfxDir[newx][newy]);
8677 DrawLevelGraphicAnimation(newx, newy, graphic);
8678 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8684 Feld[newx][newy] = EL_EMPTY;
8685 TEST_DrawLevelField(newx, newy);
8687 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8690 else if (!IS_FREE(newx, newy))
8693 if (IS_PLAYER(x, y))
8694 DrawPlayerField(x, y);
8696 TEST_DrawLevelField(x, y);
8702 else if (IS_CUSTOM_ELEMENT(element) &&
8703 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8706 if (!DigFieldByCE(newx, newy, element))
8709 int new_element = Feld[newx][newy];
8711 if (!IS_FREE(newx, newy))
8713 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8714 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8717 /* no element can dig solid indestructible elements */
8718 if (IS_INDESTRUCTIBLE(new_element) &&
8719 !IS_DIGGABLE(new_element) &&
8720 !IS_COLLECTIBLE(new_element))
8723 if (AmoebaNr[newx][newy] &&
8724 (new_element == EL_AMOEBA_FULL ||
8725 new_element == EL_BD_AMOEBA ||
8726 new_element == EL_AMOEBA_GROWING))
8728 AmoebaCnt[AmoebaNr[newx][newy]]--;
8729 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8732 if (IS_MOVING(newx, newy))
8733 RemoveMovingField(newx, newy);
8736 RemoveField(newx, newy);
8737 TEST_DrawLevelField(newx, newy);
8740 /* if digged element was about to explode, prevent the explosion */
8741 ExplodeField[newx][newy] = EX_TYPE_NONE;
8743 PlayLevelSoundAction(x, y, action);
8746 Store[newx][newy] = EL_EMPTY;
8749 /* this makes it possible to leave the removed element again */
8750 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8751 Store[newx][newy] = new_element;
8753 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8755 int move_leave_element = element_info[element].move_leave_element;
8757 /* this makes it possible to leave the removed element again */
8758 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8759 new_element : move_leave_element);
8765 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8767 RunnerVisit[x][y] = FrameCounter;
8768 PlayerVisit[x][y] /= 8; /* expire player visit path */
8771 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8773 if (!IS_FREE(newx, newy))
8775 if (IS_PLAYER(x, y))
8776 DrawPlayerField(x, y);
8778 TEST_DrawLevelField(x, y);
8784 boolean wanna_flame = !RND(10);
8785 int dx = newx - x, dy = newy - y;
8786 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8787 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8788 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8789 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8790 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8791 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8794 IS_CLASSIC_ENEMY(element1) ||
8795 IS_CLASSIC_ENEMY(element2)) &&
8796 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8797 element1 != EL_FLAMES && element2 != EL_FLAMES)
8799 ResetGfxAnimation(x, y);
8800 GfxAction[x][y] = ACTION_ATTACKING;
8802 if (IS_PLAYER(x, y))
8803 DrawPlayerField(x, y);
8805 TEST_DrawLevelField(x, y);
8807 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8809 MovDelay[x][y] = 50;
8813 RemoveField(newx, newy);
8815 Feld[newx][newy] = EL_FLAMES;
8816 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8819 RemoveField(newx1, newy1);
8821 Feld[newx1][newy1] = EL_FLAMES;
8823 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8826 RemoveField(newx2, newy2);
8828 Feld[newx2][newy2] = EL_FLAMES;
8835 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8836 Feld[newx][newy] == EL_DIAMOND)
8838 if (IS_MOVING(newx, newy))
8839 RemoveMovingField(newx, newy);
8842 Feld[newx][newy] = EL_EMPTY;
8843 TEST_DrawLevelField(newx, newy);
8846 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8848 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8849 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8851 if (AmoebaNr[newx][newy])
8853 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8854 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8855 Feld[newx][newy] == EL_BD_AMOEBA)
8856 AmoebaCnt[AmoebaNr[newx][newy]]--;
8861 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8863 RemoveMovingField(newx, newy);
8866 if (IS_MOVING(newx, newy))
8868 RemoveMovingField(newx, newy);
8873 Feld[newx][newy] = EL_EMPTY;
8874 TEST_DrawLevelField(newx, newy);
8877 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8879 else if ((element == EL_PACMAN || element == EL_MOLE)
8880 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8882 if (AmoebaNr[newx][newy])
8884 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8885 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8886 Feld[newx][newy] == EL_BD_AMOEBA)
8887 AmoebaCnt[AmoebaNr[newx][newy]]--;
8890 if (element == EL_MOLE)
8892 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8893 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8895 ResetGfxAnimation(x, y);
8896 GfxAction[x][y] = ACTION_DIGGING;
8897 TEST_DrawLevelField(x, y);
8899 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8901 return; /* wait for shrinking amoeba */
8903 else /* element == EL_PACMAN */
8905 Feld[newx][newy] = EL_EMPTY;
8906 TEST_DrawLevelField(newx, newy);
8907 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8910 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8911 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8912 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8914 /* wait for shrinking amoeba to completely disappear */
8917 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8919 /* object was running against a wall */
8924 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8925 if (move_pattern & MV_ANY_DIRECTION &&
8926 move_pattern == MovDir[x][y])
8928 int blocking_element =
8929 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8931 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8934 element = Feld[x][y]; /* element might have changed */
8938 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8939 DrawLevelElementAnimation(x, y, element);
8941 if (DONT_TOUCH(element))
8942 TestIfBadThingTouchesPlayer(x, y);
8947 InitMovingField(x, y, MovDir[x][y]);
8949 PlayLevelSoundAction(x, y, ACTION_MOVING);
8953 ContinueMoving(x, y);
8956 void ContinueMoving(int x, int y)
8958 int element = Feld[x][y];
8959 struct ElementInfo *ei = &element_info[element];
8960 int direction = MovDir[x][y];
8961 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8962 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8963 int newx = x + dx, newy = y + dy;
8964 int stored = Store[x][y];
8965 int stored_new = Store[newx][newy];
8966 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8967 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8968 boolean last_line = (newy == lev_fieldy - 1);
8970 MovPos[x][y] += getElementMoveStepsize(x, y);
8972 if (pushed_by_player) /* special case: moving object pushed by player */
8973 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8975 if (ABS(MovPos[x][y]) < TILEX)
8978 int ee = Feld[x][y];
8979 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8980 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8982 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8983 x, y, ABS(MovPos[x][y]),
8985 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8988 TEST_DrawLevelField(x, y);
8990 return; /* element is still moving */
8993 /* element reached destination field */
8995 Feld[x][y] = EL_EMPTY;
8996 Feld[newx][newy] = element;
8997 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8999 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
9001 element = Feld[newx][newy] = EL_ACID;
9003 else if (element == EL_MOLE)
9005 Feld[x][y] = EL_SAND;
9007 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9009 else if (element == EL_QUICKSAND_FILLING)
9011 element = Feld[newx][newy] = get_next_element(element);
9012 Store[newx][newy] = Store[x][y];
9014 else if (element == EL_QUICKSAND_EMPTYING)
9016 Feld[x][y] = get_next_element(element);
9017 element = Feld[newx][newy] = Store[x][y];
9019 else if (element == EL_QUICKSAND_FAST_FILLING)
9021 element = Feld[newx][newy] = get_next_element(element);
9022 Store[newx][newy] = Store[x][y];
9024 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9026 Feld[x][y] = get_next_element(element);
9027 element = Feld[newx][newy] = Store[x][y];
9029 else if (element == EL_MAGIC_WALL_FILLING)
9031 element = Feld[newx][newy] = get_next_element(element);
9032 if (!game.magic_wall_active)
9033 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9034 Store[newx][newy] = Store[x][y];
9036 else if (element == EL_MAGIC_WALL_EMPTYING)
9038 Feld[x][y] = get_next_element(element);
9039 if (!game.magic_wall_active)
9040 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9041 element = Feld[newx][newy] = Store[x][y];
9043 #if USE_NEW_CUSTOM_VALUE
9044 InitField(newx, newy, FALSE);
9047 else if (element == EL_BD_MAGIC_WALL_FILLING)
9049 element = Feld[newx][newy] = get_next_element(element);
9050 if (!game.magic_wall_active)
9051 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9052 Store[newx][newy] = Store[x][y];
9054 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9056 Feld[x][y] = get_next_element(element);
9057 if (!game.magic_wall_active)
9058 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9059 element = Feld[newx][newy] = Store[x][y];
9061 #if USE_NEW_CUSTOM_VALUE
9062 InitField(newx, newy, FALSE);
9065 else if (element == EL_DC_MAGIC_WALL_FILLING)
9067 element = Feld[newx][newy] = get_next_element(element);
9068 if (!game.magic_wall_active)
9069 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9070 Store[newx][newy] = Store[x][y];
9072 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9074 Feld[x][y] = get_next_element(element);
9075 if (!game.magic_wall_active)
9076 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9077 element = Feld[newx][newy] = Store[x][y];
9079 #if USE_NEW_CUSTOM_VALUE
9080 InitField(newx, newy, FALSE);
9083 else if (element == EL_AMOEBA_DROPPING)
9085 Feld[x][y] = get_next_element(element);
9086 element = Feld[newx][newy] = Store[x][y];
9088 else if (element == EL_SOKOBAN_OBJECT)
9091 Feld[x][y] = Back[x][y];
9093 if (Back[newx][newy])
9094 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9096 Back[x][y] = Back[newx][newy] = 0;
9099 Store[x][y] = EL_EMPTY;
9104 MovDelay[newx][newy] = 0;
9106 if (CAN_CHANGE_OR_HAS_ACTION(element))
9108 /* copy element change control values to new field */
9109 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9110 ChangePage[newx][newy] = ChangePage[x][y];
9111 ChangeCount[newx][newy] = ChangeCount[x][y];
9112 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9115 #if USE_NEW_CUSTOM_VALUE
9116 CustomValue[newx][newy] = CustomValue[x][y];
9119 ChangeDelay[x][y] = 0;
9120 ChangePage[x][y] = -1;
9121 ChangeCount[x][y] = 0;
9122 ChangeEvent[x][y] = -1;
9124 #if USE_NEW_CUSTOM_VALUE
9125 CustomValue[x][y] = 0;
9128 /* copy animation control values to new field */
9129 GfxFrame[newx][newy] = GfxFrame[x][y];
9130 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9131 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9132 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9134 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9136 /* some elements can leave other elements behind after moving */
9138 if (ei->move_leave_element != EL_EMPTY &&
9139 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9140 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9142 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9143 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9144 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9147 int move_leave_element = ei->move_leave_element;
9151 /* this makes it possible to leave the removed element again */
9152 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9153 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9155 /* this makes it possible to leave the removed element again */
9156 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9157 move_leave_element = stored;
9160 /* this makes it possible to leave the removed element again */
9161 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9162 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9163 move_leave_element = stored;
9166 Feld[x][y] = move_leave_element;
9168 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9169 MovDir[x][y] = direction;
9171 InitField(x, y, FALSE);
9173 if (GFX_CRUMBLED(Feld[x][y]))
9174 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9176 if (ELEM_IS_PLAYER(move_leave_element))
9177 RelocatePlayer(x, y, move_leave_element);
9180 /* do this after checking for left-behind element */
9181 ResetGfxAnimation(x, y); /* reset animation values for old field */
9183 if (!CAN_MOVE(element) ||
9184 (CAN_FALL(element) && direction == MV_DOWN &&
9185 (element == EL_SPRING ||
9186 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9187 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9188 GfxDir[x][y] = MovDir[newx][newy] = 0;
9190 TEST_DrawLevelField(x, y);
9191 TEST_DrawLevelField(newx, newy);
9193 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9195 /* prevent pushed element from moving on in pushed direction */
9196 if (pushed_by_player && CAN_MOVE(element) &&
9197 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9198 !(element_info[element].move_pattern & direction))
9199 TurnRound(newx, newy);
9201 /* prevent elements on conveyor belt from moving on in last direction */
9202 if (pushed_by_conveyor && CAN_FALL(element) &&
9203 direction & MV_HORIZONTAL)
9204 MovDir[newx][newy] = 0;
9206 if (!pushed_by_player)
9208 int nextx = newx + dx, nexty = newy + dy;
9209 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9211 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9213 if (CAN_FALL(element) && direction == MV_DOWN)
9214 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9216 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9217 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9219 #if USE_FIX_IMPACT_COLLISION
9220 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9221 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9225 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9227 TestIfBadThingTouchesPlayer(newx, newy);
9228 TestIfBadThingTouchesFriend(newx, newy);
9230 if (!IS_CUSTOM_ELEMENT(element))
9231 TestIfBadThingTouchesOtherBadThing(newx, newy);
9233 else if (element == EL_PENGUIN)
9234 TestIfFriendTouchesBadThing(newx, newy);
9236 if (DONT_GET_HIT_BY(element))
9238 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9241 /* give the player one last chance (one more frame) to move away */
9242 if (CAN_FALL(element) && direction == MV_DOWN &&
9243 (last_line || (!IS_FREE(x, newy + 1) &&
9244 (!IS_PLAYER(x, newy + 1) ||
9245 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9248 if (pushed_by_player && !game.use_change_when_pushing_bug)
9250 int push_side = MV_DIR_OPPOSITE(direction);
9251 struct PlayerInfo *player = PLAYERINFO(x, y);
9253 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9254 player->index_bit, push_side);
9255 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9256 player->index_bit, push_side);
9259 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9260 MovDelay[newx][newy] = 1;
9262 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9264 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9267 if (ChangePage[newx][newy] != -1) /* delayed change */
9269 int page = ChangePage[newx][newy];
9270 struct ElementChangeInfo *change = &ei->change_page[page];
9272 ChangePage[newx][newy] = -1;
9274 if (change->can_change)
9276 if (ChangeElement(newx, newy, element, page))
9278 if (change->post_change_function)
9279 change->post_change_function(newx, newy);
9283 if (change->has_action)
9284 ExecuteCustomElementAction(newx, newy, element, page);
9288 TestIfElementHitsCustomElement(newx, newy, direction);
9289 TestIfPlayerTouchesCustomElement(newx, newy);
9290 TestIfElementTouchesCustomElement(newx, newy);
9292 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9293 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9294 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9295 MV_DIR_OPPOSITE(direction));
9298 int AmoebeNachbarNr(int ax, int ay)
9301 int element = Feld[ax][ay];
9303 static int xy[4][2] =
9311 for (i = 0; i < NUM_DIRECTIONS; i++)
9313 int x = ax + xy[i][0];
9314 int y = ay + xy[i][1];
9316 if (!IN_LEV_FIELD(x, y))
9319 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9320 group_nr = AmoebaNr[x][y];
9326 void AmoebenVereinigen(int ax, int ay)
9328 int i, x, y, xx, yy;
9329 int new_group_nr = AmoebaNr[ax][ay];
9330 static int xy[4][2] =
9338 if (new_group_nr == 0)
9341 for (i = 0; i < NUM_DIRECTIONS; i++)
9346 if (!IN_LEV_FIELD(x, y))
9349 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9350 Feld[x][y] == EL_BD_AMOEBA ||
9351 Feld[x][y] == EL_AMOEBA_DEAD) &&
9352 AmoebaNr[x][y] != new_group_nr)
9354 int old_group_nr = AmoebaNr[x][y];
9356 if (old_group_nr == 0)
9359 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9360 AmoebaCnt[old_group_nr] = 0;
9361 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9362 AmoebaCnt2[old_group_nr] = 0;
9364 SCAN_PLAYFIELD(xx, yy)
9366 if (AmoebaNr[xx][yy] == old_group_nr)
9367 AmoebaNr[xx][yy] = new_group_nr;
9373 void AmoebeUmwandeln(int ax, int ay)
9377 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9379 int group_nr = AmoebaNr[ax][ay];
9384 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9385 printf("AmoebeUmwandeln(): This should never happen!\n");
9390 SCAN_PLAYFIELD(x, y)
9392 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9395 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9399 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9400 SND_AMOEBA_TURNING_TO_GEM :
9401 SND_AMOEBA_TURNING_TO_ROCK));
9406 static int xy[4][2] =
9414 for (i = 0; i < NUM_DIRECTIONS; i++)
9419 if (!IN_LEV_FIELD(x, y))
9422 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9424 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9425 SND_AMOEBA_TURNING_TO_GEM :
9426 SND_AMOEBA_TURNING_TO_ROCK));
9433 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9436 int group_nr = AmoebaNr[ax][ay];
9437 boolean done = FALSE;
9442 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9443 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9448 SCAN_PLAYFIELD(x, y)
9450 if (AmoebaNr[x][y] == group_nr &&
9451 (Feld[x][y] == EL_AMOEBA_DEAD ||
9452 Feld[x][y] == EL_BD_AMOEBA ||
9453 Feld[x][y] == EL_AMOEBA_GROWING))
9456 Feld[x][y] = new_element;
9457 InitField(x, y, FALSE);
9458 TEST_DrawLevelField(x, y);
9464 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9465 SND_BD_AMOEBA_TURNING_TO_ROCK :
9466 SND_BD_AMOEBA_TURNING_TO_GEM));
9469 void AmoebeWaechst(int x, int y)
9471 static unsigned long sound_delay = 0;
9472 static unsigned long sound_delay_value = 0;
9474 if (!MovDelay[x][y]) /* start new growing cycle */
9478 if (DelayReached(&sound_delay, sound_delay_value))
9480 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9481 sound_delay_value = 30;
9485 if (MovDelay[x][y]) /* wait some time before growing bigger */
9488 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9490 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9491 6 - MovDelay[x][y]);
9493 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9496 if (!MovDelay[x][y])
9498 Feld[x][y] = Store[x][y];
9500 TEST_DrawLevelField(x, y);
9505 void AmoebaDisappearing(int x, int y)
9507 static unsigned long sound_delay = 0;
9508 static unsigned long sound_delay_value = 0;
9510 if (!MovDelay[x][y]) /* start new shrinking cycle */
9514 if (DelayReached(&sound_delay, sound_delay_value))
9515 sound_delay_value = 30;
9518 if (MovDelay[x][y]) /* wait some time before shrinking */
9521 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9523 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9524 6 - MovDelay[x][y]);
9526 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9529 if (!MovDelay[x][y])
9531 Feld[x][y] = EL_EMPTY;
9532 TEST_DrawLevelField(x, y);
9534 /* don't let mole enter this field in this cycle;
9535 (give priority to objects falling to this field from above) */
9541 void AmoebeAbleger(int ax, int ay)
9544 int element = Feld[ax][ay];
9545 int graphic = el2img(element);
9546 int newax = ax, neway = ay;
9547 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9548 static int xy[4][2] =
9556 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9558 Feld[ax][ay] = EL_AMOEBA_DEAD;
9559 TEST_DrawLevelField(ax, ay);
9563 if (IS_ANIMATED(graphic))
9564 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9566 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9567 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9569 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9572 if (MovDelay[ax][ay])
9576 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9579 int x = ax + xy[start][0];
9580 int y = ay + xy[start][1];
9582 if (!IN_LEV_FIELD(x, y))
9585 if (IS_FREE(x, y) ||
9586 CAN_GROW_INTO(Feld[x][y]) ||
9587 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9588 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9594 if (newax == ax && neway == ay)
9597 else /* normal or "filled" (BD style) amoeba */
9600 boolean waiting_for_player = FALSE;
9602 for (i = 0; i < NUM_DIRECTIONS; i++)
9604 int j = (start + i) % 4;
9605 int x = ax + xy[j][0];
9606 int y = ay + xy[j][1];
9608 if (!IN_LEV_FIELD(x, y))
9611 if (IS_FREE(x, y) ||
9612 CAN_GROW_INTO(Feld[x][y]) ||
9613 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9614 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9620 else if (IS_PLAYER(x, y))
9621 waiting_for_player = TRUE;
9624 if (newax == ax && neway == ay) /* amoeba cannot grow */
9626 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9628 Feld[ax][ay] = EL_AMOEBA_DEAD;
9629 TEST_DrawLevelField(ax, ay);
9630 AmoebaCnt[AmoebaNr[ax][ay]]--;
9632 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9634 if (element == EL_AMOEBA_FULL)
9635 AmoebeUmwandeln(ax, ay);
9636 else if (element == EL_BD_AMOEBA)
9637 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9642 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9644 /* amoeba gets larger by growing in some direction */
9646 int new_group_nr = AmoebaNr[ax][ay];
9649 if (new_group_nr == 0)
9651 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9652 printf("AmoebeAbleger(): This should never happen!\n");
9657 AmoebaNr[newax][neway] = new_group_nr;
9658 AmoebaCnt[new_group_nr]++;
9659 AmoebaCnt2[new_group_nr]++;
9661 /* if amoeba touches other amoeba(s) after growing, unify them */
9662 AmoebenVereinigen(newax, neway);
9664 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9666 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9672 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9673 (neway == lev_fieldy - 1 && newax != ax))
9675 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9676 Store[newax][neway] = element;
9678 else if (neway == ay || element == EL_EMC_DRIPPER)
9680 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9682 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9686 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9687 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9688 Store[ax][ay] = EL_AMOEBA_DROP;
9689 ContinueMoving(ax, ay);
9693 TEST_DrawLevelField(newax, neway);
9696 void Life(int ax, int ay)
9700 int element = Feld[ax][ay];
9701 int graphic = el2img(element);
9702 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9704 boolean changed = FALSE;
9706 if (IS_ANIMATED(graphic))
9707 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9712 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9713 MovDelay[ax][ay] = life_time;
9715 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9718 if (MovDelay[ax][ay])
9722 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9724 int xx = ax+x1, yy = ay+y1;
9727 if (!IN_LEV_FIELD(xx, yy))
9730 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9732 int x = xx+x2, y = yy+y2;
9734 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9737 if (((Feld[x][y] == element ||
9738 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9740 (IS_FREE(x, y) && Stop[x][y]))
9744 if (xx == ax && yy == ay) /* field in the middle */
9746 if (nachbarn < life_parameter[0] ||
9747 nachbarn > life_parameter[1])
9749 Feld[xx][yy] = EL_EMPTY;
9751 TEST_DrawLevelField(xx, yy);
9752 Stop[xx][yy] = TRUE;
9756 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9757 { /* free border field */
9758 if (nachbarn >= life_parameter[2] &&
9759 nachbarn <= life_parameter[3])
9761 Feld[xx][yy] = element;
9762 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9764 TEST_DrawLevelField(xx, yy);
9765 Stop[xx][yy] = TRUE;
9772 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9773 SND_GAME_OF_LIFE_GROWING);
9776 static void InitRobotWheel(int x, int y)
9778 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9781 static void RunRobotWheel(int x, int y)
9783 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9786 static void StopRobotWheel(int x, int y)
9788 if (ZX == x && ZY == y)
9792 game.robot_wheel_active = FALSE;
9796 static void InitTimegateWheel(int x, int y)
9798 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9801 static void RunTimegateWheel(int x, int y)
9803 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9806 static void InitMagicBallDelay(int x, int y)
9809 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9811 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9815 static void ActivateMagicBall(int bx, int by)
9819 if (level.ball_random)
9821 int pos_border = RND(8); /* select one of the eight border elements */
9822 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9823 int xx = pos_content % 3;
9824 int yy = pos_content / 3;
9829 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9830 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9834 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9836 int xx = x - bx + 1;
9837 int yy = y - by + 1;
9839 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9840 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9844 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9847 void CheckExit(int x, int y)
9849 if (local_player->gems_still_needed > 0 ||
9850 local_player->sokobanfields_still_needed > 0 ||
9851 local_player->lights_still_needed > 0)
9853 int element = Feld[x][y];
9854 int graphic = el2img(element);
9856 if (IS_ANIMATED(graphic))
9857 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9862 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9865 Feld[x][y] = EL_EXIT_OPENING;
9867 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9870 void CheckExitEM(int x, int y)
9872 if (local_player->gems_still_needed > 0 ||
9873 local_player->sokobanfields_still_needed > 0 ||
9874 local_player->lights_still_needed > 0)
9876 int element = Feld[x][y];
9877 int graphic = el2img(element);
9879 if (IS_ANIMATED(graphic))
9880 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9885 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9888 Feld[x][y] = EL_EM_EXIT_OPENING;
9890 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9893 void CheckExitSteel(int x, int y)
9895 if (local_player->gems_still_needed > 0 ||
9896 local_player->sokobanfields_still_needed > 0 ||
9897 local_player->lights_still_needed > 0)
9899 int element = Feld[x][y];
9900 int graphic = el2img(element);
9902 if (IS_ANIMATED(graphic))
9903 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9908 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9911 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9913 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9916 void CheckExitSteelEM(int x, int y)
9918 if (local_player->gems_still_needed > 0 ||
9919 local_player->sokobanfields_still_needed > 0 ||
9920 local_player->lights_still_needed > 0)
9922 int element = Feld[x][y];
9923 int graphic = el2img(element);
9925 if (IS_ANIMATED(graphic))
9926 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9931 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9934 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9936 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9939 void CheckExitSP(int x, int y)
9941 if (local_player->gems_still_needed > 0)
9943 int element = Feld[x][y];
9944 int graphic = el2img(element);
9946 if (IS_ANIMATED(graphic))
9947 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9952 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9955 Feld[x][y] = EL_SP_EXIT_OPENING;
9957 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9960 static void CloseAllOpenTimegates()
9964 SCAN_PLAYFIELD(x, y)
9966 int element = Feld[x][y];
9968 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9970 Feld[x][y] = EL_TIMEGATE_CLOSING;
9972 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9977 void DrawTwinkleOnField(int x, int y)
9979 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9982 if (Feld[x][y] == EL_BD_DIAMOND)
9985 if (MovDelay[x][y] == 0) /* next animation frame */
9986 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9988 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9992 DrawLevelElementAnimation(x, y, Feld[x][y]);
9994 if (MovDelay[x][y] != 0)
9996 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9997 10 - MovDelay[x][y]);
9999 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10004 void MauerWaechst(int x, int y)
10008 if (!MovDelay[x][y]) /* next animation frame */
10009 MovDelay[x][y] = 3 * delay;
10011 if (MovDelay[x][y]) /* wait some time before next frame */
10015 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10017 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10018 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10020 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10023 if (!MovDelay[x][y])
10025 if (MovDir[x][y] == MV_LEFT)
10027 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10028 TEST_DrawLevelField(x - 1, y);
10030 else if (MovDir[x][y] == MV_RIGHT)
10032 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10033 TEST_DrawLevelField(x + 1, y);
10035 else if (MovDir[x][y] == MV_UP)
10037 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10038 TEST_DrawLevelField(x, y - 1);
10042 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10043 TEST_DrawLevelField(x, y + 1);
10046 Feld[x][y] = Store[x][y];
10048 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10049 TEST_DrawLevelField(x, y);
10054 void MauerAbleger(int ax, int ay)
10056 int element = Feld[ax][ay];
10057 int graphic = el2img(element);
10058 boolean oben_frei = FALSE, unten_frei = FALSE;
10059 boolean links_frei = FALSE, rechts_frei = FALSE;
10060 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10061 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10062 boolean new_wall = FALSE;
10064 if (IS_ANIMATED(graphic))
10065 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10067 if (!MovDelay[ax][ay]) /* start building new wall */
10068 MovDelay[ax][ay] = 6;
10070 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10072 MovDelay[ax][ay]--;
10073 if (MovDelay[ax][ay])
10077 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10079 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10081 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10083 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10084 rechts_frei = TRUE;
10086 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10087 element == EL_EXPANDABLE_WALL_ANY)
10091 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10092 Store[ax][ay-1] = element;
10093 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10094 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10095 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10096 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10101 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10102 Store[ax][ay+1] = element;
10103 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10104 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10105 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10106 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10111 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10112 element == EL_EXPANDABLE_WALL_ANY ||
10113 element == EL_EXPANDABLE_WALL ||
10114 element == EL_BD_EXPANDABLE_WALL)
10118 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10119 Store[ax-1][ay] = element;
10120 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10121 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10122 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10123 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10129 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10130 Store[ax+1][ay] = element;
10131 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10132 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10133 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10134 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10139 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10140 TEST_DrawLevelField(ax, ay);
10142 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10143 oben_massiv = TRUE;
10144 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10145 unten_massiv = TRUE;
10146 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10147 links_massiv = TRUE;
10148 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10149 rechts_massiv = TRUE;
10151 if (((oben_massiv && unten_massiv) ||
10152 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10153 element == EL_EXPANDABLE_WALL) &&
10154 ((links_massiv && rechts_massiv) ||
10155 element == EL_EXPANDABLE_WALL_VERTICAL))
10156 Feld[ax][ay] = EL_WALL;
10159 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10162 void MauerAblegerStahl(int ax, int ay)
10164 int element = Feld[ax][ay];
10165 int graphic = el2img(element);
10166 boolean oben_frei = FALSE, unten_frei = FALSE;
10167 boolean links_frei = FALSE, rechts_frei = FALSE;
10168 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10169 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10170 boolean new_wall = FALSE;
10172 if (IS_ANIMATED(graphic))
10173 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10175 if (!MovDelay[ax][ay]) /* start building new wall */
10176 MovDelay[ax][ay] = 6;
10178 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10180 MovDelay[ax][ay]--;
10181 if (MovDelay[ax][ay])
10185 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10187 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10189 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10191 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10192 rechts_frei = TRUE;
10194 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10195 element == EL_EXPANDABLE_STEELWALL_ANY)
10199 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10200 Store[ax][ay-1] = element;
10201 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10202 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10203 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10204 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10209 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10210 Store[ax][ay+1] = element;
10211 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10212 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10213 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10214 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10219 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10220 element == EL_EXPANDABLE_STEELWALL_ANY)
10224 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10225 Store[ax-1][ay] = element;
10226 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10227 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10228 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10229 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10235 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10236 Store[ax+1][ay] = element;
10237 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10238 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10239 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10240 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10245 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10246 oben_massiv = TRUE;
10247 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10248 unten_massiv = TRUE;
10249 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10250 links_massiv = TRUE;
10251 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10252 rechts_massiv = TRUE;
10254 if (((oben_massiv && unten_massiv) ||
10255 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10256 ((links_massiv && rechts_massiv) ||
10257 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10258 Feld[ax][ay] = EL_STEELWALL;
10261 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10264 void CheckForDragon(int x, int y)
10267 boolean dragon_found = FALSE;
10268 static int xy[4][2] =
10276 for (i = 0; i < NUM_DIRECTIONS; i++)
10278 for (j = 0; j < 4; j++)
10280 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10282 if (IN_LEV_FIELD(xx, yy) &&
10283 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10285 if (Feld[xx][yy] == EL_DRAGON)
10286 dragon_found = TRUE;
10295 for (i = 0; i < NUM_DIRECTIONS; i++)
10297 for (j = 0; j < 3; j++)
10299 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10301 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10303 Feld[xx][yy] = EL_EMPTY;
10304 TEST_DrawLevelField(xx, yy);
10313 static void InitBuggyBase(int x, int y)
10315 int element = Feld[x][y];
10316 int activating_delay = FRAMES_PER_SECOND / 4;
10318 ChangeDelay[x][y] =
10319 (element == EL_SP_BUGGY_BASE ?
10320 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10321 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10323 element == EL_SP_BUGGY_BASE_ACTIVE ?
10324 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10327 static void WarnBuggyBase(int x, int y)
10330 static int xy[4][2] =
10338 for (i = 0; i < NUM_DIRECTIONS; i++)
10340 int xx = x + xy[i][0];
10341 int yy = y + xy[i][1];
10343 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10345 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10352 static void InitTrap(int x, int y)
10354 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10357 static void ActivateTrap(int x, int y)
10359 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10362 static void ChangeActiveTrap(int x, int y)
10364 int graphic = IMG_TRAP_ACTIVE;
10366 /* if new animation frame was drawn, correct crumbled sand border */
10367 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10368 TEST_DrawLevelFieldCrumbledSand(x, y);
10371 static int getSpecialActionElement(int element, int number, int base_element)
10373 return (element != EL_EMPTY ? element :
10374 number != -1 ? base_element + number - 1 :
10378 static int getModifiedActionNumber(int value_old, int operator, int operand,
10379 int value_min, int value_max)
10381 int value_new = (operator == CA_MODE_SET ? operand :
10382 operator == CA_MODE_ADD ? value_old + operand :
10383 operator == CA_MODE_SUBTRACT ? value_old - operand :
10384 operator == CA_MODE_MULTIPLY ? value_old * operand :
10385 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10386 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10389 return (value_new < value_min ? value_min :
10390 value_new > value_max ? value_max :
10394 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10396 struct ElementInfo *ei = &element_info[element];
10397 struct ElementChangeInfo *change = &ei->change_page[page];
10398 int target_element = change->target_element;
10399 int action_type = change->action_type;
10400 int action_mode = change->action_mode;
10401 int action_arg = change->action_arg;
10402 int action_element = change->action_element;
10405 if (!change->has_action)
10408 /* ---------- determine action paramater values -------------------------- */
10410 int level_time_value =
10411 (level.time > 0 ? TimeLeft :
10414 int action_arg_element_raw =
10415 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10416 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10417 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10418 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10419 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10420 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10421 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10423 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10426 if (action_arg_element_raw == EL_GROUP_START)
10427 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10430 int action_arg_direction =
10431 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10432 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10433 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10434 change->actual_trigger_side :
10435 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10436 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10439 int action_arg_number_min =
10440 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10443 int action_arg_number_max =
10444 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10445 action_type == CA_SET_LEVEL_GEMS ? 999 :
10446 action_type == CA_SET_LEVEL_TIME ? 9999 :
10447 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10448 action_type == CA_SET_CE_VALUE ? 9999 :
10449 action_type == CA_SET_CE_SCORE ? 9999 :
10452 int action_arg_number_reset =
10453 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10454 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10455 action_type == CA_SET_LEVEL_TIME ? level.time :
10456 action_type == CA_SET_LEVEL_SCORE ? 0 :
10457 #if USE_NEW_CUSTOM_VALUE
10458 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10460 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10462 action_type == CA_SET_CE_SCORE ? 0 :
10465 int action_arg_number =
10466 (action_arg <= CA_ARG_MAX ? action_arg :
10467 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10468 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10469 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10470 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10471 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10472 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10473 #if USE_NEW_CUSTOM_VALUE
10474 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10476 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10478 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10479 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10480 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10481 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10482 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10483 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10484 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10485 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10486 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10487 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10488 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10489 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10490 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10491 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10494 int action_arg_number_old =
10495 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10496 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10497 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10498 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10499 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10502 int action_arg_number_new =
10503 getModifiedActionNumber(action_arg_number_old,
10504 action_mode, action_arg_number,
10505 action_arg_number_min, action_arg_number_max);
10508 int trigger_player_bits =
10509 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10510 change->actual_trigger_player_bits : change->trigger_player);
10512 int trigger_player_bits =
10513 (change->actual_trigger_player >= EL_PLAYER_1 &&
10514 change->actual_trigger_player <= EL_PLAYER_4 ?
10515 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10519 int action_arg_player_bits =
10520 (action_arg >= CA_ARG_PLAYER_1 &&
10521 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10522 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10523 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10526 /* ---------- execute action -------------------------------------------- */
10528 switch (action_type)
10535 /* ---------- level actions ------------------------------------------- */
10537 case CA_RESTART_LEVEL:
10539 game.restart_level = TRUE;
10544 case CA_SHOW_ENVELOPE:
10546 int element = getSpecialActionElement(action_arg_element,
10547 action_arg_number, EL_ENVELOPE_1);
10549 if (IS_ENVELOPE(element))
10550 local_player->show_envelope = element;
10555 case CA_SET_LEVEL_TIME:
10557 if (level.time > 0) /* only modify limited time value */
10559 TimeLeft = action_arg_number_new;
10562 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10564 DisplayGameControlValues();
10566 DrawGameValue_Time(TimeLeft);
10569 if (!TimeLeft && setup.time_limit)
10570 for (i = 0; i < MAX_PLAYERS; i++)
10571 KillPlayer(&stored_player[i]);
10577 case CA_SET_LEVEL_SCORE:
10579 local_player->score = action_arg_number_new;
10582 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10584 DisplayGameControlValues();
10586 DrawGameValue_Score(local_player->score);
10592 case CA_SET_LEVEL_GEMS:
10594 local_player->gems_still_needed = action_arg_number_new;
10597 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10599 DisplayGameControlValues();
10601 DrawGameValue_Emeralds(local_player->gems_still_needed);
10607 #if !USE_PLAYER_GRAVITY
10608 case CA_SET_LEVEL_GRAVITY:
10610 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10611 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10612 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10618 case CA_SET_LEVEL_WIND:
10620 game.wind_direction = action_arg_direction;
10625 case CA_SET_LEVEL_RANDOM_SEED:
10628 /* ensure that setting a new random seed while playing is predictable */
10629 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10631 InitRND(action_arg_number_new);
10635 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10643 for (i = 0; i < 9; i++)
10644 printf("%d, ", RND(2));
10652 /* ---------- player actions ------------------------------------------ */
10654 case CA_MOVE_PLAYER:
10656 /* automatically move to the next field in specified direction */
10657 for (i = 0; i < MAX_PLAYERS; i++)
10658 if (trigger_player_bits & (1 << i))
10659 stored_player[i].programmed_action = action_arg_direction;
10664 case CA_EXIT_PLAYER:
10666 for (i = 0; i < MAX_PLAYERS; i++)
10667 if (action_arg_player_bits & (1 << i))
10668 PlayerWins(&stored_player[i]);
10673 case CA_KILL_PLAYER:
10675 for (i = 0; i < MAX_PLAYERS; i++)
10676 if (action_arg_player_bits & (1 << i))
10677 KillPlayer(&stored_player[i]);
10682 case CA_SET_PLAYER_KEYS:
10684 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10685 int element = getSpecialActionElement(action_arg_element,
10686 action_arg_number, EL_KEY_1);
10688 if (IS_KEY(element))
10690 for (i = 0; i < MAX_PLAYERS; i++)
10692 if (trigger_player_bits & (1 << i))
10694 stored_player[i].key[KEY_NR(element)] = key_state;
10696 DrawGameDoorValues();
10704 case CA_SET_PLAYER_SPEED:
10707 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10710 for (i = 0; i < MAX_PLAYERS; i++)
10712 if (trigger_player_bits & (1 << i))
10714 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10716 if (action_arg == CA_ARG_SPEED_FASTER &&
10717 stored_player[i].cannot_move)
10719 action_arg_number = STEPSIZE_VERY_SLOW;
10721 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10722 action_arg == CA_ARG_SPEED_FASTER)
10724 action_arg_number = 2;
10725 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10728 else if (action_arg == CA_ARG_NUMBER_RESET)
10730 action_arg_number = level.initial_player_stepsize[i];
10734 getModifiedActionNumber(move_stepsize,
10737 action_arg_number_min,
10738 action_arg_number_max);
10740 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10747 case CA_SET_PLAYER_SHIELD:
10749 for (i = 0; i < MAX_PLAYERS; i++)
10751 if (trigger_player_bits & (1 << i))
10753 if (action_arg == CA_ARG_SHIELD_OFF)
10755 stored_player[i].shield_normal_time_left = 0;
10756 stored_player[i].shield_deadly_time_left = 0;
10758 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10760 stored_player[i].shield_normal_time_left = 999999;
10762 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10764 stored_player[i].shield_normal_time_left = 999999;
10765 stored_player[i].shield_deadly_time_left = 999999;
10773 #if USE_PLAYER_GRAVITY
10774 case CA_SET_PLAYER_GRAVITY:
10776 for (i = 0; i < MAX_PLAYERS; i++)
10778 if (trigger_player_bits & (1 << i))
10780 stored_player[i].gravity =
10781 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10782 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10783 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10784 stored_player[i].gravity);
10792 case CA_SET_PLAYER_ARTWORK:
10794 for (i = 0; i < MAX_PLAYERS; i++)
10796 if (trigger_player_bits & (1 << i))
10798 int artwork_element = action_arg_element;
10800 if (action_arg == CA_ARG_ELEMENT_RESET)
10802 (level.use_artwork_element[i] ? level.artwork_element[i] :
10803 stored_player[i].element_nr);
10805 #if USE_GFX_RESET_PLAYER_ARTWORK
10806 if (stored_player[i].artwork_element != artwork_element)
10807 stored_player[i].Frame = 0;
10810 stored_player[i].artwork_element = artwork_element;
10812 SetPlayerWaiting(&stored_player[i], FALSE);
10814 /* set number of special actions for bored and sleeping animation */
10815 stored_player[i].num_special_action_bored =
10816 get_num_special_action(artwork_element,
10817 ACTION_BORING_1, ACTION_BORING_LAST);
10818 stored_player[i].num_special_action_sleeping =
10819 get_num_special_action(artwork_element,
10820 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10827 case CA_SET_PLAYER_INVENTORY:
10829 for (i = 0; i < MAX_PLAYERS; i++)
10831 struct PlayerInfo *player = &stored_player[i];
10834 if (trigger_player_bits & (1 << i))
10836 int inventory_element = action_arg_element;
10838 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10839 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10840 action_arg == CA_ARG_ELEMENT_ACTION)
10842 int element = inventory_element;
10843 int collect_count = element_info[element].collect_count_initial;
10845 if (!IS_CUSTOM_ELEMENT(element))
10848 if (collect_count == 0)
10849 player->inventory_infinite_element = element;
10851 for (k = 0; k < collect_count; k++)
10852 if (player->inventory_size < MAX_INVENTORY_SIZE)
10853 player->inventory_element[player->inventory_size++] =
10856 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10857 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10858 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10860 if (player->inventory_infinite_element != EL_UNDEFINED &&
10861 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10862 action_arg_element_raw))
10863 player->inventory_infinite_element = EL_UNDEFINED;
10865 for (k = 0, j = 0; j < player->inventory_size; j++)
10867 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10868 action_arg_element_raw))
10869 player->inventory_element[k++] = player->inventory_element[j];
10872 player->inventory_size = k;
10874 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10876 if (player->inventory_size > 0)
10878 for (j = 0; j < player->inventory_size - 1; j++)
10879 player->inventory_element[j] = player->inventory_element[j + 1];
10881 player->inventory_size--;
10884 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10886 if (player->inventory_size > 0)
10887 player->inventory_size--;
10889 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10891 player->inventory_infinite_element = EL_UNDEFINED;
10892 player->inventory_size = 0;
10894 else if (action_arg == CA_ARG_INVENTORY_RESET)
10896 player->inventory_infinite_element = EL_UNDEFINED;
10897 player->inventory_size = 0;
10899 if (level.use_initial_inventory[i])
10901 for (j = 0; j < level.initial_inventory_size[i]; j++)
10903 int element = level.initial_inventory_content[i][j];
10904 int collect_count = element_info[element].collect_count_initial;
10906 if (!IS_CUSTOM_ELEMENT(element))
10909 if (collect_count == 0)
10910 player->inventory_infinite_element = element;
10912 for (k = 0; k < collect_count; k++)
10913 if (player->inventory_size < MAX_INVENTORY_SIZE)
10914 player->inventory_element[player->inventory_size++] =
10925 /* ---------- CE actions ---------------------------------------------- */
10927 case CA_SET_CE_VALUE:
10929 #if USE_NEW_CUSTOM_VALUE
10930 int last_ce_value = CustomValue[x][y];
10932 CustomValue[x][y] = action_arg_number_new;
10934 if (CustomValue[x][y] != last_ce_value)
10936 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10937 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10939 if (CustomValue[x][y] == 0)
10941 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10942 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10950 case CA_SET_CE_SCORE:
10952 #if USE_NEW_CUSTOM_VALUE
10953 int last_ce_score = ei->collect_score;
10955 ei->collect_score = action_arg_number_new;
10957 if (ei->collect_score != last_ce_score)
10959 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10960 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10962 if (ei->collect_score == 0)
10966 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10967 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10970 This is a very special case that seems to be a mixture between
10971 CheckElementChange() and CheckTriggeredElementChange(): while
10972 the first one only affects single elements that are triggered
10973 directly, the second one affects multiple elements in the playfield
10974 that are triggered indirectly by another element. This is a third
10975 case: Changing the CE score always affects multiple identical CEs,
10976 so every affected CE must be checked, not only the single CE for
10977 which the CE score was changed in the first place (as every instance
10978 of that CE shares the same CE score, and therefore also can change)!
10980 SCAN_PLAYFIELD(xx, yy)
10982 if (Feld[xx][yy] == element)
10983 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10984 CE_SCORE_GETS_ZERO);
10993 case CA_SET_CE_ARTWORK:
10995 int artwork_element = action_arg_element;
10996 boolean reset_frame = FALSE;
10999 if (action_arg == CA_ARG_ELEMENT_RESET)
11000 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11003 if (ei->gfx_element != artwork_element)
11004 reset_frame = TRUE;
11006 ei->gfx_element = artwork_element;
11008 SCAN_PLAYFIELD(xx, yy)
11010 if (Feld[xx][yy] == element)
11014 ResetGfxAnimation(xx, yy);
11015 ResetRandomAnimationValue(xx, yy);
11018 TEST_DrawLevelField(xx, yy);
11025 /* ---------- engine actions ------------------------------------------ */
11027 case CA_SET_ENGINE_SCAN_MODE:
11029 InitPlayfieldScanMode(action_arg);
11039 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11041 int old_element = Feld[x][y];
11042 int new_element = GetElementFromGroupElement(element);
11043 int previous_move_direction = MovDir[x][y];
11044 #if USE_NEW_CUSTOM_VALUE
11045 int last_ce_value = CustomValue[x][y];
11047 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11048 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11049 boolean add_player_onto_element = (new_element_is_player &&
11050 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11051 /* this breaks SnakeBite when a snake is
11052 halfway through a door that closes */
11053 /* NOW FIXED AT LEVEL INIT IN files.c */
11054 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11056 IS_WALKABLE(old_element));
11059 /* check if element under the player changes from accessible to unaccessible
11060 (needed for special case of dropping element which then changes) */
11061 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11062 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11070 if (!add_player_onto_element)
11072 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11073 RemoveMovingField(x, y);
11077 Feld[x][y] = new_element;
11079 #if !USE_GFX_RESET_GFX_ANIMATION
11080 ResetGfxAnimation(x, y);
11081 ResetRandomAnimationValue(x, y);
11084 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11085 MovDir[x][y] = previous_move_direction;
11087 #if USE_NEW_CUSTOM_VALUE
11088 if (element_info[new_element].use_last_ce_value)
11089 CustomValue[x][y] = last_ce_value;
11092 InitField_WithBug1(x, y, FALSE);
11094 new_element = Feld[x][y]; /* element may have changed */
11096 #if USE_GFX_RESET_GFX_ANIMATION
11097 ResetGfxAnimation(x, y);
11098 ResetRandomAnimationValue(x, y);
11101 TEST_DrawLevelField(x, y);
11103 if (GFX_CRUMBLED(new_element))
11104 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11108 /* check if element under the player changes from accessible to unaccessible
11109 (needed for special case of dropping element which then changes) */
11110 /* (must be checked after creating new element for walkable group elements) */
11111 #if USE_FIX_KILLED_BY_NON_WALKABLE
11112 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11113 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11120 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11121 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11130 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11131 if (new_element_is_player)
11132 RelocatePlayer(x, y, new_element);
11135 ChangeCount[x][y]++; /* count number of changes in the same frame */
11137 TestIfBadThingTouchesPlayer(x, y);
11138 TestIfPlayerTouchesCustomElement(x, y);
11139 TestIfElementTouchesCustomElement(x, y);
11142 static void CreateField(int x, int y, int element)
11144 CreateFieldExt(x, y, element, FALSE);
11147 static void CreateElementFromChange(int x, int y, int element)
11149 element = GET_VALID_RUNTIME_ELEMENT(element);
11151 #if USE_STOP_CHANGED_ELEMENTS
11152 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11154 int old_element = Feld[x][y];
11156 /* prevent changed element from moving in same engine frame
11157 unless both old and new element can either fall or move */
11158 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11159 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11164 CreateFieldExt(x, y, element, TRUE);
11167 static boolean ChangeElement(int x, int y, int element, int page)
11169 struct ElementInfo *ei = &element_info[element];
11170 struct ElementChangeInfo *change = &ei->change_page[page];
11171 int ce_value = CustomValue[x][y];
11172 int ce_score = ei->collect_score;
11173 int target_element;
11174 int old_element = Feld[x][y];
11176 /* always use default change event to prevent running into a loop */
11177 if (ChangeEvent[x][y] == -1)
11178 ChangeEvent[x][y] = CE_DELAY;
11180 if (ChangeEvent[x][y] == CE_DELAY)
11182 /* reset actual trigger element, trigger player and action element */
11183 change->actual_trigger_element = EL_EMPTY;
11184 change->actual_trigger_player = EL_EMPTY;
11185 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11186 change->actual_trigger_side = CH_SIDE_NONE;
11187 change->actual_trigger_ce_value = 0;
11188 change->actual_trigger_ce_score = 0;
11191 /* do not change elements more than a specified maximum number of changes */
11192 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11195 ChangeCount[x][y]++; /* count number of changes in the same frame */
11197 if (change->explode)
11204 if (change->use_target_content)
11206 boolean complete_replace = TRUE;
11207 boolean can_replace[3][3];
11210 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11213 boolean is_walkable;
11214 boolean is_diggable;
11215 boolean is_collectible;
11216 boolean is_removable;
11217 boolean is_destructible;
11218 int ex = x + xx - 1;
11219 int ey = y + yy - 1;
11220 int content_element = change->target_content.e[xx][yy];
11223 can_replace[xx][yy] = TRUE;
11225 if (ex == x && ey == y) /* do not check changing element itself */
11228 if (content_element == EL_EMPTY_SPACE)
11230 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11235 if (!IN_LEV_FIELD(ex, ey))
11237 can_replace[xx][yy] = FALSE;
11238 complete_replace = FALSE;
11245 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11246 e = MovingOrBlocked2Element(ex, ey);
11248 is_empty = (IS_FREE(ex, ey) ||
11249 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11251 is_walkable = (is_empty || IS_WALKABLE(e));
11252 is_diggable = (is_empty || IS_DIGGABLE(e));
11253 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11254 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11255 is_removable = (is_diggable || is_collectible);
11257 can_replace[xx][yy] =
11258 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11259 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11260 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11261 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11262 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11263 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11264 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11266 if (!can_replace[xx][yy])
11267 complete_replace = FALSE;
11270 if (!change->only_if_complete || complete_replace)
11272 boolean something_has_changed = FALSE;
11274 if (change->only_if_complete && change->use_random_replace &&
11275 RND(100) < change->random_percentage)
11278 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11280 int ex = x + xx - 1;
11281 int ey = y + yy - 1;
11282 int content_element;
11284 if (can_replace[xx][yy] && (!change->use_random_replace ||
11285 RND(100) < change->random_percentage))
11287 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11288 RemoveMovingField(ex, ey);
11290 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11292 content_element = change->target_content.e[xx][yy];
11293 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11294 ce_value, ce_score);
11296 CreateElementFromChange(ex, ey, target_element);
11298 something_has_changed = TRUE;
11300 /* for symmetry reasons, freeze newly created border elements */
11301 if (ex != x || ey != y)
11302 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11306 if (something_has_changed)
11308 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11309 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11315 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11316 ce_value, ce_score);
11318 if (element == EL_DIAGONAL_GROWING ||
11319 element == EL_DIAGONAL_SHRINKING)
11321 target_element = Store[x][y];
11323 Store[x][y] = EL_EMPTY;
11326 CreateElementFromChange(x, y, target_element);
11328 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11329 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11332 /* this uses direct change before indirect change */
11333 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11338 #if USE_NEW_DELAYED_ACTION
11340 static void HandleElementChange(int x, int y, int page)
11342 int element = MovingOrBlocked2Element(x, y);
11343 struct ElementInfo *ei = &element_info[element];
11344 struct ElementChangeInfo *change = &ei->change_page[page];
11345 boolean handle_action_before_change = FALSE;
11348 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11349 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11352 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11353 x, y, element, element_info[element].token_name);
11354 printf("HandleElementChange(): This should never happen!\n");
11359 /* this can happen with classic bombs on walkable, changing elements */
11360 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11363 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11364 ChangeDelay[x][y] = 0;
11370 if (ChangeDelay[x][y] == 0) /* initialize element change */
11372 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11374 if (change->can_change)
11377 /* !!! not clear why graphic animation should be reset at all here !!! */
11378 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11379 #if USE_GFX_RESET_WHEN_NOT_MOVING
11380 /* when a custom element is about to change (for example by change delay),
11381 do not reset graphic animation when the custom element is moving */
11382 if (!IS_MOVING(x, y))
11385 ResetGfxAnimation(x, y);
11386 ResetRandomAnimationValue(x, y);
11390 if (change->pre_change_function)
11391 change->pre_change_function(x, y);
11395 ChangeDelay[x][y]--;
11397 if (ChangeDelay[x][y] != 0) /* continue element change */
11399 if (change->can_change)
11401 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11403 if (IS_ANIMATED(graphic))
11404 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11406 if (change->change_function)
11407 change->change_function(x, y);
11410 else /* finish element change */
11412 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11414 page = ChangePage[x][y];
11415 ChangePage[x][y] = -1;
11417 change = &ei->change_page[page];
11420 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11422 ChangeDelay[x][y] = 1; /* try change after next move step */
11423 ChangePage[x][y] = page; /* remember page to use for change */
11429 /* special case: set new level random seed before changing element */
11430 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11431 handle_action_before_change = TRUE;
11433 if (change->has_action && handle_action_before_change)
11434 ExecuteCustomElementAction(x, y, element, page);
11437 if (change->can_change)
11439 if (ChangeElement(x, y, element, page))
11441 if (change->post_change_function)
11442 change->post_change_function(x, y);
11446 if (change->has_action && !handle_action_before_change)
11447 ExecuteCustomElementAction(x, y, element, page);
11453 static void HandleElementChange(int x, int y, int page)
11455 int element = MovingOrBlocked2Element(x, y);
11456 struct ElementInfo *ei = &element_info[element];
11457 struct ElementChangeInfo *change = &ei->change_page[page];
11460 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11463 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11464 x, y, element, element_info[element].token_name);
11465 printf("HandleElementChange(): This should never happen!\n");
11470 /* this can happen with classic bombs on walkable, changing elements */
11471 if (!CAN_CHANGE(element))
11474 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11475 ChangeDelay[x][y] = 0;
11481 if (ChangeDelay[x][y] == 0) /* initialize element change */
11483 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11485 ResetGfxAnimation(x, y);
11486 ResetRandomAnimationValue(x, y);
11488 if (change->pre_change_function)
11489 change->pre_change_function(x, y);
11492 ChangeDelay[x][y]--;
11494 if (ChangeDelay[x][y] != 0) /* continue element change */
11496 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11498 if (IS_ANIMATED(graphic))
11499 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11501 if (change->change_function)
11502 change->change_function(x, y);
11504 else /* finish element change */
11506 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11508 page = ChangePage[x][y];
11509 ChangePage[x][y] = -1;
11511 change = &ei->change_page[page];
11514 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11516 ChangeDelay[x][y] = 1; /* try change after next move step */
11517 ChangePage[x][y] = page; /* remember page to use for change */
11522 if (ChangeElement(x, y, element, page))
11524 if (change->post_change_function)
11525 change->post_change_function(x, y);
11532 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11533 int trigger_element,
11535 int trigger_player,
11539 boolean change_done_any = FALSE;
11540 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11543 if (!(trigger_events[trigger_element][trigger_event]))
11547 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11548 trigger_event, recursion_loop_depth, recursion_loop_detected,
11549 recursion_loop_element, EL_NAME(recursion_loop_element));
11552 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11554 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11556 int element = EL_CUSTOM_START + i;
11557 boolean change_done = FALSE;
11560 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11561 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11564 for (p = 0; p < element_info[element].num_change_pages; p++)
11566 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11568 if (change->can_change_or_has_action &&
11569 change->has_event[trigger_event] &&
11570 change->trigger_side & trigger_side &&
11571 change->trigger_player & trigger_player &&
11572 change->trigger_page & trigger_page_bits &&
11573 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11575 change->actual_trigger_element = trigger_element;
11576 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11577 change->actual_trigger_player_bits = trigger_player;
11578 change->actual_trigger_side = trigger_side;
11579 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11580 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11583 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11584 element, EL_NAME(element), p);
11587 if ((change->can_change && !change_done) || change->has_action)
11591 SCAN_PLAYFIELD(x, y)
11593 if (Feld[x][y] == element)
11595 if (change->can_change && !change_done)
11597 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11598 /* if element already changed in this frame, not only prevent
11599 another element change (checked in ChangeElement()), but
11600 also prevent additional element actions for this element */
11602 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11603 !level.use_action_after_change_bug)
11608 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11609 element, EL_NAME(element), p);
11612 ChangeDelay[x][y] = 1;
11613 ChangeEvent[x][y] = trigger_event;
11615 HandleElementChange(x, y, p);
11617 #if USE_NEW_DELAYED_ACTION
11618 else if (change->has_action)
11620 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11621 /* if element already changed in this frame, not only prevent
11622 another element change (checked in ChangeElement()), but
11623 also prevent additional element actions for this element */
11625 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11626 !level.use_action_after_change_bug)
11632 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11633 element, EL_NAME(element), p);
11636 ExecuteCustomElementAction(x, y, element, p);
11637 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11640 if (change->has_action)
11642 ExecuteCustomElementAction(x, y, element, p);
11643 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11649 if (change->can_change)
11651 change_done = TRUE;
11652 change_done_any = TRUE;
11655 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11656 element, EL_NAME(element), p);
11665 RECURSION_LOOP_DETECTION_END();
11667 return change_done_any;
11670 static boolean CheckElementChangeExt(int x, int y,
11672 int trigger_element,
11674 int trigger_player,
11677 boolean change_done = FALSE;
11680 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11681 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11684 if (Feld[x][y] == EL_BLOCKED)
11686 Blocked2Moving(x, y, &x, &y);
11687 element = Feld[x][y];
11691 /* check if element has already changed */
11692 if (Feld[x][y] != element)
11695 /* check if element has already changed or is about to change after moving */
11696 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11697 Feld[x][y] != element) ||
11699 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11700 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11701 ChangePage[x][y] != -1)))
11706 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11707 trigger_event, recursion_loop_depth, recursion_loop_detected,
11708 recursion_loop_element, EL_NAME(recursion_loop_element));
11711 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11714 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11717 for (p = 0; p < element_info[element].num_change_pages; p++)
11719 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11721 /* check trigger element for all events where the element that is checked
11722 for changing interacts with a directly adjacent element -- this is
11723 different to element changes that affect other elements to change on the
11724 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11725 boolean check_trigger_element =
11726 (trigger_event == CE_TOUCHING_X ||
11727 trigger_event == CE_HITTING_X ||
11728 trigger_event == CE_HIT_BY_X ||
11730 /* this one was forgotten until 3.2.3 */
11731 trigger_event == CE_DIGGING_X);
11734 if (change->can_change_or_has_action &&
11735 change->has_event[trigger_event] &&
11736 change->trigger_side & trigger_side &&
11737 change->trigger_player & trigger_player &&
11738 (!check_trigger_element ||
11739 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11741 change->actual_trigger_element = trigger_element;
11742 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11743 change->actual_trigger_player_bits = trigger_player;
11744 change->actual_trigger_side = trigger_side;
11745 change->actual_trigger_ce_value = CustomValue[x][y];
11746 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11748 /* special case: trigger element not at (x,y) position for some events */
11749 if (check_trigger_element)
11761 { 0, 0 }, { 0, 0 }, { 0, 0 },
11765 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11766 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11768 change->actual_trigger_ce_value = CustomValue[xx][yy];
11769 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11772 if (change->can_change && !change_done)
11774 ChangeDelay[x][y] = 1;
11775 ChangeEvent[x][y] = trigger_event;
11777 HandleElementChange(x, y, p);
11779 change_done = TRUE;
11781 #if USE_NEW_DELAYED_ACTION
11782 else if (change->has_action)
11784 ExecuteCustomElementAction(x, y, element, p);
11785 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11788 if (change->has_action)
11790 ExecuteCustomElementAction(x, y, element, p);
11791 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11797 RECURSION_LOOP_DETECTION_END();
11799 return change_done;
11802 static void PlayPlayerSound(struct PlayerInfo *player)
11804 int jx = player->jx, jy = player->jy;
11805 int sound_element = player->artwork_element;
11806 int last_action = player->last_action_waiting;
11807 int action = player->action_waiting;
11809 if (player->is_waiting)
11811 if (action != last_action)
11812 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11814 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11818 if (action != last_action)
11819 StopSound(element_info[sound_element].sound[last_action]);
11821 if (last_action == ACTION_SLEEPING)
11822 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11826 static void PlayAllPlayersSound()
11830 for (i = 0; i < MAX_PLAYERS; i++)
11831 if (stored_player[i].active)
11832 PlayPlayerSound(&stored_player[i]);
11835 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11837 boolean last_waiting = player->is_waiting;
11838 int move_dir = player->MovDir;
11840 player->dir_waiting = move_dir;
11841 player->last_action_waiting = player->action_waiting;
11845 if (!last_waiting) /* not waiting -> waiting */
11847 player->is_waiting = TRUE;
11849 player->frame_counter_bored =
11851 game.player_boring_delay_fixed +
11852 GetSimpleRandom(game.player_boring_delay_random);
11853 player->frame_counter_sleeping =
11855 game.player_sleeping_delay_fixed +
11856 GetSimpleRandom(game.player_sleeping_delay_random);
11858 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11861 if (game.player_sleeping_delay_fixed +
11862 game.player_sleeping_delay_random > 0 &&
11863 player->anim_delay_counter == 0 &&
11864 player->post_delay_counter == 0 &&
11865 FrameCounter >= player->frame_counter_sleeping)
11866 player->is_sleeping = TRUE;
11867 else if (game.player_boring_delay_fixed +
11868 game.player_boring_delay_random > 0 &&
11869 FrameCounter >= player->frame_counter_bored)
11870 player->is_bored = TRUE;
11872 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11873 player->is_bored ? ACTION_BORING :
11876 if (player->is_sleeping && player->use_murphy)
11878 /* special case for sleeping Murphy when leaning against non-free tile */
11880 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11881 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11882 !IS_MOVING(player->jx - 1, player->jy)))
11883 move_dir = MV_LEFT;
11884 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11885 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11886 !IS_MOVING(player->jx + 1, player->jy)))
11887 move_dir = MV_RIGHT;
11889 player->is_sleeping = FALSE;
11891 player->dir_waiting = move_dir;
11894 if (player->is_sleeping)
11896 if (player->num_special_action_sleeping > 0)
11898 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11900 int last_special_action = player->special_action_sleeping;
11901 int num_special_action = player->num_special_action_sleeping;
11902 int special_action =
11903 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11904 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11905 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11906 last_special_action + 1 : ACTION_SLEEPING);
11907 int special_graphic =
11908 el_act_dir2img(player->artwork_element, special_action, move_dir);
11910 player->anim_delay_counter =
11911 graphic_info[special_graphic].anim_delay_fixed +
11912 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11913 player->post_delay_counter =
11914 graphic_info[special_graphic].post_delay_fixed +
11915 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11917 player->special_action_sleeping = special_action;
11920 if (player->anim_delay_counter > 0)
11922 player->action_waiting = player->special_action_sleeping;
11923 player->anim_delay_counter--;
11925 else if (player->post_delay_counter > 0)
11927 player->post_delay_counter--;
11931 else if (player->is_bored)
11933 if (player->num_special_action_bored > 0)
11935 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11937 int special_action =
11938 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11939 int special_graphic =
11940 el_act_dir2img(player->artwork_element, special_action, move_dir);
11942 player->anim_delay_counter =
11943 graphic_info[special_graphic].anim_delay_fixed +
11944 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11945 player->post_delay_counter =
11946 graphic_info[special_graphic].post_delay_fixed +
11947 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11949 player->special_action_bored = special_action;
11952 if (player->anim_delay_counter > 0)
11954 player->action_waiting = player->special_action_bored;
11955 player->anim_delay_counter--;
11957 else if (player->post_delay_counter > 0)
11959 player->post_delay_counter--;
11964 else if (last_waiting) /* waiting -> not waiting */
11966 player->is_waiting = FALSE;
11967 player->is_bored = FALSE;
11968 player->is_sleeping = FALSE;
11970 player->frame_counter_bored = -1;
11971 player->frame_counter_sleeping = -1;
11973 player->anim_delay_counter = 0;
11974 player->post_delay_counter = 0;
11976 player->dir_waiting = player->MovDir;
11977 player->action_waiting = ACTION_DEFAULT;
11979 player->special_action_bored = ACTION_DEFAULT;
11980 player->special_action_sleeping = ACTION_DEFAULT;
11984 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11986 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11987 int left = player_action & JOY_LEFT;
11988 int right = player_action & JOY_RIGHT;
11989 int up = player_action & JOY_UP;
11990 int down = player_action & JOY_DOWN;
11991 int button1 = player_action & JOY_BUTTON_1;
11992 int button2 = player_action & JOY_BUTTON_2;
11993 int dx = (left ? -1 : right ? 1 : 0);
11994 int dy = (up ? -1 : down ? 1 : 0);
11996 if (!player->active || tape.pausing)
12002 snapped = SnapField(player, dx, dy);
12006 dropped = DropElement(player);
12008 moved = MovePlayer(player, dx, dy);
12011 if (tape.single_step && tape.recording && !tape.pausing)
12014 /* as it is called "single step mode", just return to pause mode when the
12015 player stopped moving after one tile (or never starts moving at all) */
12016 if (!player->is_moving)
12018 /* this is buggy: there are quite some cases where the single step mode
12019 does not return to pause mode (like pushing things that don't move
12020 or simply by trying to run against a wall) */
12021 if (button1 || (dropped && !moved))
12024 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12025 SnapField(player, 0, 0); /* stop snapping */
12029 SetPlayerWaiting(player, FALSE);
12031 return player_action;
12035 /* no actions for this player (no input at player's configured device) */
12037 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12038 SnapField(player, 0, 0);
12039 CheckGravityMovementWhenNotMoving(player);
12041 if (player->MovPos == 0)
12042 SetPlayerWaiting(player, TRUE);
12044 if (player->MovPos == 0) /* needed for tape.playing */
12045 player->is_moving = FALSE;
12047 player->is_dropping = FALSE;
12048 player->is_dropping_pressed = FALSE;
12049 player->drop_pressed_delay = 0;
12055 static void CheckLevelTime()
12059 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12060 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12062 if (level.native_em_level->lev->home == 0) /* all players at home */
12064 PlayerWins(local_player);
12066 AllPlayersGone = TRUE;
12068 level.native_em_level->lev->home = -1;
12071 if (level.native_em_level->ply[0]->alive == 0 &&
12072 level.native_em_level->ply[1]->alive == 0 &&
12073 level.native_em_level->ply[2]->alive == 0 &&
12074 level.native_em_level->ply[3]->alive == 0) /* all dead */
12075 AllPlayersGone = TRUE;
12077 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12079 if (game_sp.LevelSolved &&
12080 !game_sp.GameOver) /* game won */
12082 PlayerWins(local_player);
12084 game_sp.GameOver = TRUE;
12086 AllPlayersGone = TRUE;
12089 if (game_sp.GameOver) /* game lost */
12090 AllPlayersGone = TRUE;
12093 if (TimeFrames >= FRAMES_PER_SECOND)
12098 for (i = 0; i < MAX_PLAYERS; i++)
12100 struct PlayerInfo *player = &stored_player[i];
12102 if (SHIELD_ON(player))
12104 player->shield_normal_time_left--;
12106 if (player->shield_deadly_time_left > 0)
12107 player->shield_deadly_time_left--;
12111 if (!local_player->LevelSolved && !level.use_step_counter)
12119 if (TimeLeft <= 10 && setup.time_limit)
12120 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12123 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12125 DisplayGameControlValues();
12127 DrawGameValue_Time(TimeLeft);
12130 if (!TimeLeft && setup.time_limit)
12132 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12133 level.native_em_level->lev->killed_out_of_time = TRUE;
12135 for (i = 0; i < MAX_PLAYERS; i++)
12136 KillPlayer(&stored_player[i]);
12140 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12142 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12144 DisplayGameControlValues();
12147 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12148 DrawGameValue_Time(TimePlayed);
12151 level.native_em_level->lev->time =
12152 (level.time == 0 ? TimePlayed : TimeLeft);
12155 if (tape.recording || tape.playing)
12156 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12160 UpdateAndDisplayGameControlValues();
12162 UpdateGameDoorValues();
12163 DrawGameDoorValues();
12167 void AdvanceFrameAndPlayerCounters(int player_nr)
12171 /* advance frame counters (global frame counter and time frame counter) */
12175 /* advance player counters (counters for move delay, move animation etc.) */
12176 for (i = 0; i < MAX_PLAYERS; i++)
12178 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12179 int move_delay_value = stored_player[i].move_delay_value;
12180 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12182 if (!advance_player_counters) /* not all players may be affected */
12185 #if USE_NEW_PLAYER_ANIM
12186 if (move_frames == 0) /* less than one move per game frame */
12188 int stepsize = TILEX / move_delay_value;
12189 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12190 int count = (stored_player[i].is_moving ?
12191 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12193 if (count % delay == 0)
12198 stored_player[i].Frame += move_frames;
12200 if (stored_player[i].MovPos != 0)
12201 stored_player[i].StepFrame += move_frames;
12203 if (stored_player[i].move_delay > 0)
12204 stored_player[i].move_delay--;
12206 /* due to bugs in previous versions, counter must count up, not down */
12207 if (stored_player[i].push_delay != -1)
12208 stored_player[i].push_delay++;
12210 if (stored_player[i].drop_delay > 0)
12211 stored_player[i].drop_delay--;
12213 if (stored_player[i].is_dropping_pressed)
12214 stored_player[i].drop_pressed_delay++;
12218 void StartGameActions(boolean init_network_game, boolean record_tape,
12221 unsigned long new_random_seed = InitRND(random_seed);
12224 TapeStartRecording(new_random_seed);
12226 #if defined(NETWORK_AVALIABLE)
12227 if (init_network_game)
12229 SendToServer_StartPlaying();
12240 static unsigned long game_frame_delay = 0;
12241 unsigned long game_frame_delay_value;
12242 byte *recorded_player_action;
12243 byte summarized_player_action = 0;
12244 byte tape_action[MAX_PLAYERS];
12247 /* detect endless loops, caused by custom element programming */
12248 if (recursion_loop_detected && recursion_loop_depth == 0)
12250 char *message = getStringCat3("Internal Error ! Element ",
12251 EL_NAME(recursion_loop_element),
12252 " caused endless loop ! Quit the game ?");
12254 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12255 EL_NAME(recursion_loop_element));
12257 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12259 recursion_loop_detected = FALSE; /* if game should be continued */
12266 if (game.restart_level)
12267 StartGameActions(options.network, setup.autorecord, level.random_seed);
12269 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12270 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12272 if (level.native_em_level->lev->home == 0) /* all players at home */
12274 PlayerWins(local_player);
12276 AllPlayersGone = TRUE;
12278 level.native_em_level->lev->home = -1;
12281 if (level.native_em_level->ply[0]->alive == 0 &&
12282 level.native_em_level->ply[1]->alive == 0 &&
12283 level.native_em_level->ply[2]->alive == 0 &&
12284 level.native_em_level->ply[3]->alive == 0) /* all dead */
12285 AllPlayersGone = TRUE;
12287 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12289 if (game_sp.LevelSolved &&
12290 !game_sp.GameOver) /* game won */
12292 PlayerWins(local_player);
12294 game_sp.GameOver = TRUE;
12296 AllPlayersGone = TRUE;
12299 if (game_sp.GameOver) /* game lost */
12300 AllPlayersGone = TRUE;
12303 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12306 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12309 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12312 game_frame_delay_value =
12313 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12315 if (tape.playing && tape.warp_forward && !tape.pausing)
12316 game_frame_delay_value = 0;
12318 /* ---------- main game synchronization point ---------- */
12320 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12322 if (network_playing && !network_player_action_received)
12324 /* try to get network player actions in time */
12326 #if defined(NETWORK_AVALIABLE)
12327 /* last chance to get network player actions without main loop delay */
12328 HandleNetworking();
12331 /* game was quit by network peer */
12332 if (game_status != GAME_MODE_PLAYING)
12335 if (!network_player_action_received)
12336 return; /* failed to get network player actions in time */
12338 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12344 /* at this point we know that we really continue executing the game */
12346 network_player_action_received = FALSE;
12348 /* when playing tape, read previously recorded player input from tape data */
12349 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12352 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12357 if (tape.set_centered_player)
12359 game.centered_player_nr_next = tape.centered_player_nr_next;
12360 game.set_centered_player = TRUE;
12363 for (i = 0; i < MAX_PLAYERS; i++)
12365 summarized_player_action |= stored_player[i].action;
12367 if (!network_playing)
12368 stored_player[i].effective_action = stored_player[i].action;
12371 #if defined(NETWORK_AVALIABLE)
12372 if (network_playing)
12373 SendToServer_MovePlayer(summarized_player_action);
12376 if (!options.network && !setup.team_mode)
12377 local_player->effective_action = summarized_player_action;
12379 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12381 for (i = 0; i < MAX_PLAYERS; i++)
12382 stored_player[i].effective_action =
12383 (i == game.centered_player_nr ? summarized_player_action : 0);
12386 if (recorded_player_action != NULL)
12387 for (i = 0; i < MAX_PLAYERS; i++)
12388 stored_player[i].effective_action = recorded_player_action[i];
12390 for (i = 0; i < MAX_PLAYERS; i++)
12392 tape_action[i] = stored_player[i].effective_action;
12394 /* (this can only happen in the R'n'D game engine) */
12395 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12396 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12399 /* only record actions from input devices, but not programmed actions */
12400 if (tape.recording)
12401 TapeRecordAction(tape_action);
12403 #if USE_NEW_PLAYER_ASSIGNMENTS
12405 byte mapped_action[MAX_PLAYERS];
12407 for (i = 0; i < MAX_PLAYERS; i++)
12408 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12410 for (i = 0; i < MAX_PLAYERS; i++)
12411 stored_player[i].effective_action = mapped_action[i];
12415 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12417 GameActions_EM_Main();
12419 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12421 GameActions_SP_Main();
12429 void GameActions_EM_Main()
12431 byte effective_action[MAX_PLAYERS];
12432 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12435 for (i = 0; i < MAX_PLAYERS; i++)
12436 effective_action[i] = stored_player[i].effective_action;
12438 GameActions_EM(effective_action, warp_mode);
12442 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12445 void GameActions_SP_Main()
12447 byte effective_action[MAX_PLAYERS];
12448 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12451 for (i = 0; i < MAX_PLAYERS; i++)
12452 effective_action[i] = stored_player[i].effective_action;
12454 GameActions_SP(effective_action, warp_mode);
12458 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12461 void GameActions_RND()
12463 int magic_wall_x = 0, magic_wall_y = 0;
12464 int i, x, y, element, graphic;
12466 InitPlayfieldScanModeVars();
12468 #if USE_ONE_MORE_CHANGE_PER_FRAME
12469 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12471 SCAN_PLAYFIELD(x, y)
12473 ChangeCount[x][y] = 0;
12474 ChangeEvent[x][y] = -1;
12479 if (game.set_centered_player)
12481 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12483 /* switching to "all players" only possible if all players fit to screen */
12484 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12486 game.centered_player_nr_next = game.centered_player_nr;
12487 game.set_centered_player = FALSE;
12490 /* do not switch focus to non-existing (or non-active) player */
12491 if (game.centered_player_nr_next >= 0 &&
12492 !stored_player[game.centered_player_nr_next].active)
12494 game.centered_player_nr_next = game.centered_player_nr;
12495 game.set_centered_player = FALSE;
12499 if (game.set_centered_player &&
12500 ScreenMovPos == 0) /* screen currently aligned at tile position */
12504 if (game.centered_player_nr_next == -1)
12506 setScreenCenteredToAllPlayers(&sx, &sy);
12510 sx = stored_player[game.centered_player_nr_next].jx;
12511 sy = stored_player[game.centered_player_nr_next].jy;
12514 game.centered_player_nr = game.centered_player_nr_next;
12515 game.set_centered_player = FALSE;
12517 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12518 DrawGameDoorValues();
12521 for (i = 0; i < MAX_PLAYERS; i++)
12523 int actual_player_action = stored_player[i].effective_action;
12526 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12527 - rnd_equinox_tetrachloride 048
12528 - rnd_equinox_tetrachloride_ii 096
12529 - rnd_emanuel_schmieg 002
12530 - doctor_sloan_ww 001, 020
12532 if (stored_player[i].MovPos == 0)
12533 CheckGravityMovement(&stored_player[i]);
12536 /* overwrite programmed action with tape action */
12537 if (stored_player[i].programmed_action)
12538 actual_player_action = stored_player[i].programmed_action;
12540 PlayerActions(&stored_player[i], actual_player_action);
12542 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12545 ScrollScreen(NULL, SCROLL_GO_ON);
12547 /* for backwards compatibility, the following code emulates a fixed bug that
12548 occured when pushing elements (causing elements that just made their last
12549 pushing step to already (if possible) make their first falling step in the
12550 same game frame, which is bad); this code is also needed to use the famous
12551 "spring push bug" which is used in older levels and might be wanted to be
12552 used also in newer levels, but in this case the buggy pushing code is only
12553 affecting the "spring" element and no other elements */
12555 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12557 for (i = 0; i < MAX_PLAYERS; i++)
12559 struct PlayerInfo *player = &stored_player[i];
12560 int x = player->jx;
12561 int y = player->jy;
12563 if (player->active && player->is_pushing && player->is_moving &&
12565 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12566 Feld[x][y] == EL_SPRING))
12568 ContinueMoving(x, y);
12570 /* continue moving after pushing (this is actually a bug) */
12571 if (!IS_MOVING(x, y))
12572 Stop[x][y] = FALSE;
12578 debug_print_timestamp(0, "start main loop profiling");
12581 SCAN_PLAYFIELD(x, y)
12583 ChangeCount[x][y] = 0;
12584 ChangeEvent[x][y] = -1;
12586 /* this must be handled before main playfield loop */
12587 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12590 if (MovDelay[x][y] <= 0)
12594 #if USE_NEW_SNAP_DELAY
12595 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12598 if (MovDelay[x][y] <= 0)
12601 TEST_DrawLevelField(x, y);
12603 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12609 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12611 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12612 printf("GameActions(): This should never happen!\n");
12614 ChangePage[x][y] = -1;
12618 Stop[x][y] = FALSE;
12619 if (WasJustMoving[x][y] > 0)
12620 WasJustMoving[x][y]--;
12621 if (WasJustFalling[x][y] > 0)
12622 WasJustFalling[x][y]--;
12623 if (CheckCollision[x][y] > 0)
12624 CheckCollision[x][y]--;
12625 if (CheckImpact[x][y] > 0)
12626 CheckImpact[x][y]--;
12630 /* reset finished pushing action (not done in ContinueMoving() to allow
12631 continuous pushing animation for elements with zero push delay) */
12632 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12634 ResetGfxAnimation(x, y);
12635 TEST_DrawLevelField(x, y);
12639 if (IS_BLOCKED(x, y))
12643 Blocked2Moving(x, y, &oldx, &oldy);
12644 if (!IS_MOVING(oldx, oldy))
12646 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12647 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12648 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12649 printf("GameActions(): This should never happen!\n");
12656 debug_print_timestamp(0, "- time for pre-main loop:");
12659 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12660 SCAN_PLAYFIELD(x, y)
12662 element = Feld[x][y];
12663 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12668 int element2 = element;
12669 int graphic2 = graphic;
12671 int element2 = Feld[x][y];
12672 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12674 int last_gfx_frame = GfxFrame[x][y];
12676 if (graphic_info[graphic2].anim_global_sync)
12677 GfxFrame[x][y] = FrameCounter;
12678 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12679 GfxFrame[x][y] = CustomValue[x][y];
12680 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12681 GfxFrame[x][y] = element_info[element2].collect_score;
12682 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12683 GfxFrame[x][y] = ChangeDelay[x][y];
12685 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12686 DrawLevelGraphicAnimation(x, y, graphic2);
12689 ResetGfxFrame(x, y, TRUE);
12693 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12694 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12695 ResetRandomAnimationValue(x, y);
12699 SetRandomAnimationValue(x, y);
12703 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12706 #endif // -------------------- !!! TEST ONLY !!! --------------------
12709 debug_print_timestamp(0, "- time for TEST loop: -->");
12712 SCAN_PLAYFIELD(x, y)
12714 element = Feld[x][y];
12715 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12717 ResetGfxFrame(x, y, TRUE);
12719 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12720 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12721 ResetRandomAnimationValue(x, y);
12723 SetRandomAnimationValue(x, y);
12725 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12727 if (IS_INACTIVE(element))
12729 if (IS_ANIMATED(graphic))
12730 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12735 /* this may take place after moving, so 'element' may have changed */
12736 if (IS_CHANGING(x, y) &&
12737 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12739 int page = element_info[element].event_page_nr[CE_DELAY];
12742 HandleElementChange(x, y, page);
12744 if (CAN_CHANGE(element))
12745 HandleElementChange(x, y, page);
12747 if (HAS_ACTION(element))
12748 ExecuteCustomElementAction(x, y, element, page);
12751 element = Feld[x][y];
12752 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12755 #if 0 // ---------------------------------------------------------------------
12757 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12761 element = Feld[x][y];
12762 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12764 if (IS_ANIMATED(graphic) &&
12765 !IS_MOVING(x, y) &&
12767 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12769 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12770 TEST_DrawTwinkleOnField(x, y);
12772 else if (IS_MOVING(x, y))
12773 ContinueMoving(x, y);
12780 case EL_EM_EXIT_OPEN:
12781 case EL_SP_EXIT_OPEN:
12782 case EL_STEEL_EXIT_OPEN:
12783 case EL_EM_STEEL_EXIT_OPEN:
12784 case EL_SP_TERMINAL:
12785 case EL_SP_TERMINAL_ACTIVE:
12786 case EL_EXTRA_TIME:
12787 case EL_SHIELD_NORMAL:
12788 case EL_SHIELD_DEADLY:
12789 if (IS_ANIMATED(graphic))
12790 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12793 case EL_DYNAMITE_ACTIVE:
12794 case EL_EM_DYNAMITE_ACTIVE:
12795 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12796 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12797 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12798 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12799 case EL_SP_DISK_RED_ACTIVE:
12800 CheckDynamite(x, y);
12803 case EL_AMOEBA_GROWING:
12804 AmoebeWaechst(x, y);
12807 case EL_AMOEBA_SHRINKING:
12808 AmoebaDisappearing(x, y);
12811 #if !USE_NEW_AMOEBA_CODE
12812 case EL_AMOEBA_WET:
12813 case EL_AMOEBA_DRY:
12814 case EL_AMOEBA_FULL:
12816 case EL_EMC_DRIPPER:
12817 AmoebeAbleger(x, y);
12821 case EL_GAME_OF_LIFE:
12826 case EL_EXIT_CLOSED:
12830 case EL_EM_EXIT_CLOSED:
12834 case EL_STEEL_EXIT_CLOSED:
12835 CheckExitSteel(x, y);
12838 case EL_EM_STEEL_EXIT_CLOSED:
12839 CheckExitSteelEM(x, y);
12842 case EL_SP_EXIT_CLOSED:
12846 case EL_EXPANDABLE_WALL_GROWING:
12847 case EL_EXPANDABLE_STEELWALL_GROWING:
12848 MauerWaechst(x, y);
12851 case EL_EXPANDABLE_WALL:
12852 case EL_EXPANDABLE_WALL_HORIZONTAL:
12853 case EL_EXPANDABLE_WALL_VERTICAL:
12854 case EL_EXPANDABLE_WALL_ANY:
12855 case EL_BD_EXPANDABLE_WALL:
12856 MauerAbleger(x, y);
12859 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12860 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12861 case EL_EXPANDABLE_STEELWALL_ANY:
12862 MauerAblegerStahl(x, y);
12866 CheckForDragon(x, y);
12872 case EL_ELEMENT_SNAPPING:
12873 case EL_DIAGONAL_SHRINKING:
12874 case EL_DIAGONAL_GROWING:
12877 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12879 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12884 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12885 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12890 #else // ---------------------------------------------------------------------
12892 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12896 element = Feld[x][y];
12897 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12899 if (IS_ANIMATED(graphic) &&
12900 !IS_MOVING(x, y) &&
12902 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12904 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12905 TEST_DrawTwinkleOnField(x, y);
12907 else if ((element == EL_ACID ||
12908 element == EL_EXIT_OPEN ||
12909 element == EL_EM_EXIT_OPEN ||
12910 element == EL_SP_EXIT_OPEN ||
12911 element == EL_STEEL_EXIT_OPEN ||
12912 element == EL_EM_STEEL_EXIT_OPEN ||
12913 element == EL_SP_TERMINAL ||
12914 element == EL_SP_TERMINAL_ACTIVE ||
12915 element == EL_EXTRA_TIME ||
12916 element == EL_SHIELD_NORMAL ||
12917 element == EL_SHIELD_DEADLY) &&
12918 IS_ANIMATED(graphic))
12919 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920 else if (IS_MOVING(x, y))
12921 ContinueMoving(x, y);
12922 else if (IS_ACTIVE_BOMB(element))
12923 CheckDynamite(x, y);
12924 else if (element == EL_AMOEBA_GROWING)
12925 AmoebeWaechst(x, y);
12926 else if (element == EL_AMOEBA_SHRINKING)
12927 AmoebaDisappearing(x, y);
12929 #if !USE_NEW_AMOEBA_CODE
12930 else if (IS_AMOEBALIVE(element))
12931 AmoebeAbleger(x, y);
12934 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12936 else if (element == EL_EXIT_CLOSED)
12938 else if (element == EL_EM_EXIT_CLOSED)
12940 else if (element == EL_STEEL_EXIT_CLOSED)
12941 CheckExitSteel(x, y);
12942 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12943 CheckExitSteelEM(x, y);
12944 else if (element == EL_SP_EXIT_CLOSED)
12946 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12947 element == EL_EXPANDABLE_STEELWALL_GROWING)
12948 MauerWaechst(x, y);
12949 else if (element == EL_EXPANDABLE_WALL ||
12950 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12951 element == EL_EXPANDABLE_WALL_VERTICAL ||
12952 element == EL_EXPANDABLE_WALL_ANY ||
12953 element == EL_BD_EXPANDABLE_WALL)
12954 MauerAbleger(x, y);
12955 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12956 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12957 element == EL_EXPANDABLE_STEELWALL_ANY)
12958 MauerAblegerStahl(x, y);
12959 else if (element == EL_FLAMES)
12960 CheckForDragon(x, y);
12961 else if (element == EL_EXPLOSION)
12962 ; /* drawing of correct explosion animation is handled separately */
12963 else if (element == EL_ELEMENT_SNAPPING ||
12964 element == EL_DIAGONAL_SHRINKING ||
12965 element == EL_DIAGONAL_GROWING)
12967 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12969 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12971 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12974 #endif // ---------------------------------------------------------------------
12976 if (IS_BELT_ACTIVE(element))
12977 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12979 if (game.magic_wall_active)
12981 int jx = local_player->jx, jy = local_player->jy;
12983 /* play the element sound at the position nearest to the player */
12984 if ((element == EL_MAGIC_WALL_FULL ||
12985 element == EL_MAGIC_WALL_ACTIVE ||
12986 element == EL_MAGIC_WALL_EMPTYING ||
12987 element == EL_BD_MAGIC_WALL_FULL ||
12988 element == EL_BD_MAGIC_WALL_ACTIVE ||
12989 element == EL_BD_MAGIC_WALL_EMPTYING ||
12990 element == EL_DC_MAGIC_WALL_FULL ||
12991 element == EL_DC_MAGIC_WALL_ACTIVE ||
12992 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12993 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13002 debug_print_timestamp(0, "- time for MAIN loop: -->");
13005 #if USE_NEW_AMOEBA_CODE
13006 /* new experimental amoeba growth stuff */
13007 if (!(FrameCounter % 8))
13009 static unsigned long random = 1684108901;
13011 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13013 x = RND(lev_fieldx);
13014 y = RND(lev_fieldy);
13015 element = Feld[x][y];
13017 if (!IS_PLAYER(x,y) &&
13018 (element == EL_EMPTY ||
13019 CAN_GROW_INTO(element) ||
13020 element == EL_QUICKSAND_EMPTY ||
13021 element == EL_QUICKSAND_FAST_EMPTY ||
13022 element == EL_ACID_SPLASH_LEFT ||
13023 element == EL_ACID_SPLASH_RIGHT))
13025 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13026 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13027 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13028 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13029 Feld[x][y] = EL_AMOEBA_DROP;
13032 random = random * 129 + 1;
13038 if (game.explosions_delayed)
13041 game.explosions_delayed = FALSE;
13043 SCAN_PLAYFIELD(x, y)
13045 element = Feld[x][y];
13047 if (ExplodeField[x][y])
13048 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13049 else if (element == EL_EXPLOSION)
13050 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13052 ExplodeField[x][y] = EX_TYPE_NONE;
13055 game.explosions_delayed = TRUE;
13058 if (game.magic_wall_active)
13060 if (!(game.magic_wall_time_left % 4))
13062 int element = Feld[magic_wall_x][magic_wall_y];
13064 if (element == EL_BD_MAGIC_WALL_FULL ||
13065 element == EL_BD_MAGIC_WALL_ACTIVE ||
13066 element == EL_BD_MAGIC_WALL_EMPTYING)
13067 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13068 else if (element == EL_DC_MAGIC_WALL_FULL ||
13069 element == EL_DC_MAGIC_WALL_ACTIVE ||
13070 element == EL_DC_MAGIC_WALL_EMPTYING)
13071 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13073 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13076 if (game.magic_wall_time_left > 0)
13078 game.magic_wall_time_left--;
13080 if (!game.magic_wall_time_left)
13082 SCAN_PLAYFIELD(x, y)
13084 element = Feld[x][y];
13086 if (element == EL_MAGIC_WALL_ACTIVE ||
13087 element == EL_MAGIC_WALL_FULL)
13089 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13090 TEST_DrawLevelField(x, y);
13092 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13093 element == EL_BD_MAGIC_WALL_FULL)
13095 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13096 TEST_DrawLevelField(x, y);
13098 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13099 element == EL_DC_MAGIC_WALL_FULL)
13101 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13102 TEST_DrawLevelField(x, y);
13106 game.magic_wall_active = FALSE;
13111 if (game.light_time_left > 0)
13113 game.light_time_left--;
13115 if (game.light_time_left == 0)
13116 RedrawAllLightSwitchesAndInvisibleElements();
13119 if (game.timegate_time_left > 0)
13121 game.timegate_time_left--;
13123 if (game.timegate_time_left == 0)
13124 CloseAllOpenTimegates();
13127 if (game.lenses_time_left > 0)
13129 game.lenses_time_left--;
13131 if (game.lenses_time_left == 0)
13132 RedrawAllInvisibleElementsForLenses();
13135 if (game.magnify_time_left > 0)
13137 game.magnify_time_left--;
13139 if (game.magnify_time_left == 0)
13140 RedrawAllInvisibleElementsForMagnifier();
13143 for (i = 0; i < MAX_PLAYERS; i++)
13145 struct PlayerInfo *player = &stored_player[i];
13147 if (SHIELD_ON(player))
13149 if (player->shield_deadly_time_left)
13150 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13151 else if (player->shield_normal_time_left)
13152 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13156 #if USE_DELAYED_GFX_REDRAW
13157 SCAN_PLAYFIELD(x, y)
13160 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13162 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13163 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13166 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13167 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13169 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13170 DrawLevelField(x, y);
13172 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13173 DrawLevelFieldCrumbledSand(x, y);
13175 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13176 DrawLevelFieldCrumbledSandNeighbours(x, y);
13178 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13179 DrawTwinkleOnField(x, y);
13182 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13189 PlayAllPlayersSound();
13191 if (options.debug) /* calculate frames per second */
13193 static unsigned long fps_counter = 0;
13194 static int fps_frames = 0;
13195 unsigned long fps_delay_ms = Counter() - fps_counter;
13199 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13201 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13204 fps_counter = Counter();
13207 redraw_mask |= REDRAW_FPS;
13210 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13212 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13214 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13216 local_player->show_envelope = 0;
13220 debug_print_timestamp(0, "stop main loop profiling ");
13221 printf("----------------------------------------------------------\n");
13224 /* use random number generator in every frame to make it less predictable */
13225 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13229 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13231 int min_x = x, min_y = y, max_x = x, max_y = y;
13234 for (i = 0; i < MAX_PLAYERS; i++)
13236 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13238 if (!stored_player[i].active || &stored_player[i] == player)
13241 min_x = MIN(min_x, jx);
13242 min_y = MIN(min_y, jy);
13243 max_x = MAX(max_x, jx);
13244 max_y = MAX(max_y, jy);
13247 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13250 static boolean AllPlayersInVisibleScreen()
13254 for (i = 0; i < MAX_PLAYERS; i++)
13256 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13258 if (!stored_player[i].active)
13261 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13268 void ScrollLevel(int dx, int dy)
13271 /* (directly solved in BlitBitmap() now) */
13272 static Bitmap *bitmap_db_field2 = NULL;
13273 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13280 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13281 /* only horizontal XOR vertical scroll direction allowed */
13282 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13287 /* (directly solved in BlitBitmap() now) */
13288 if (bitmap_db_field2 == NULL)
13289 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13291 /* needed when blitting directly to same bitmap -- should not be needed with
13292 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13293 BlitBitmap(drawto_field, bitmap_db_field2,
13294 FX + TILEX * (dx == -1) - softscroll_offset,
13295 FY + TILEY * (dy == -1) - softscroll_offset,
13296 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13297 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13298 FX + TILEX * (dx == 1) - softscroll_offset,
13299 FY + TILEY * (dy == 1) - softscroll_offset);
13300 BlitBitmap(bitmap_db_field2, drawto_field,
13301 FX + TILEX * (dx == 1) - softscroll_offset,
13302 FY + TILEY * (dy == 1) - softscroll_offset,
13303 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13304 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13305 FX + TILEX * (dx == 1) - softscroll_offset,
13306 FY + TILEY * (dy == 1) - softscroll_offset);
13311 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13312 int xsize = (BX2 - BX1 + 1);
13313 int ysize = (BY2 - BY1 + 1);
13314 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13315 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13316 int step = (start < end ? +1 : -1);
13318 for (i = start; i != end; i += step)
13320 BlitBitmap(drawto_field, drawto_field,
13321 FX + TILEX * (dx != 0 ? i + step : 0),
13322 FY + TILEY * (dy != 0 ? i + step : 0),
13323 TILEX * (dx != 0 ? 1 : xsize),
13324 TILEY * (dy != 0 ? 1 : ysize),
13325 FX + TILEX * (dx != 0 ? i : 0),
13326 FY + TILEY * (dy != 0 ? i : 0));
13331 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13333 BlitBitmap(drawto_field, drawto_field,
13334 FX + TILEX * (dx == -1) - softscroll_offset,
13335 FY + TILEY * (dy == -1) - softscroll_offset,
13336 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13337 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13338 FX + TILEX * (dx == 1) - softscroll_offset,
13339 FY + TILEY * (dy == 1) - softscroll_offset);
13345 x = (dx == 1 ? BX1 : BX2);
13346 for (y = BY1; y <= BY2; y++)
13347 DrawScreenField(x, y);
13352 y = (dy == 1 ? BY1 : BY2);
13353 for (x = BX1; x <= BX2; x++)
13354 DrawScreenField(x, y);
13357 redraw_mask |= REDRAW_FIELD;
13360 static boolean canFallDown(struct PlayerInfo *player)
13362 int jx = player->jx, jy = player->jy;
13364 return (IN_LEV_FIELD(jx, jy + 1) &&
13365 (IS_FREE(jx, jy + 1) ||
13366 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13367 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13368 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13371 static boolean canPassField(int x, int y, int move_dir)
13373 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13374 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13375 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13376 int nextx = x + dx;
13377 int nexty = y + dy;
13378 int element = Feld[x][y];
13380 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13381 !CAN_MOVE(element) &&
13382 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13383 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13384 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13387 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13389 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13390 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13391 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13395 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13396 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13397 (IS_DIGGABLE(Feld[newx][newy]) ||
13398 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13399 canPassField(newx, newy, move_dir)));
13402 static void CheckGravityMovement(struct PlayerInfo *player)
13404 #if USE_PLAYER_GRAVITY
13405 if (player->gravity && !player->programmed_action)
13407 if (game.gravity && !player->programmed_action)
13410 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13411 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13412 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13413 int jx = player->jx, jy = player->jy;
13414 boolean player_is_moving_to_valid_field =
13415 (!player_is_snapping &&
13416 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13417 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13418 boolean player_can_fall_down = canFallDown(player);
13420 if (player_can_fall_down &&
13421 !player_is_moving_to_valid_field)
13422 player->programmed_action = MV_DOWN;
13426 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13428 return CheckGravityMovement(player);
13430 #if USE_PLAYER_GRAVITY
13431 if (player->gravity && !player->programmed_action)
13433 if (game.gravity && !player->programmed_action)
13436 int jx = player->jx, jy = player->jy;
13437 boolean field_under_player_is_free =
13438 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13439 boolean player_is_standing_on_valid_field =
13440 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13441 (IS_WALKABLE(Feld[jx][jy]) &&
13442 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13444 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13445 player->programmed_action = MV_DOWN;
13450 MovePlayerOneStep()
13451 -----------------------------------------------------------------------------
13452 dx, dy: direction (non-diagonal) to try to move the player to
13453 real_dx, real_dy: direction as read from input device (can be diagonal)
13456 boolean MovePlayerOneStep(struct PlayerInfo *player,
13457 int dx, int dy, int real_dx, int real_dy)
13459 int jx = player->jx, jy = player->jy;
13460 int new_jx = jx + dx, new_jy = jy + dy;
13461 #if !USE_FIXED_DONT_RUN_INTO
13465 boolean player_can_move = !player->cannot_move;
13467 if (!player->active || (!dx && !dy))
13468 return MP_NO_ACTION;
13470 player->MovDir = (dx < 0 ? MV_LEFT :
13471 dx > 0 ? MV_RIGHT :
13473 dy > 0 ? MV_DOWN : MV_NONE);
13475 if (!IN_LEV_FIELD(new_jx, new_jy))
13476 return MP_NO_ACTION;
13478 if (!player_can_move)
13480 if (player->MovPos == 0)
13482 player->is_moving = FALSE;
13483 player->is_digging = FALSE;
13484 player->is_collecting = FALSE;
13485 player->is_snapping = FALSE;
13486 player->is_pushing = FALSE;
13491 if (!options.network && game.centered_player_nr == -1 &&
13492 !AllPlayersInSight(player, new_jx, new_jy))
13493 return MP_NO_ACTION;
13495 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13496 return MP_NO_ACTION;
13499 #if !USE_FIXED_DONT_RUN_INTO
13500 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13502 /* (moved to DigField()) */
13503 if (player_can_move && DONT_RUN_INTO(element))
13505 if (element == EL_ACID && dx == 0 && dy == 1)
13507 SplashAcid(new_jx, new_jy);
13508 Feld[jx][jy] = EL_PLAYER_1;
13509 InitMovingField(jx, jy, MV_DOWN);
13510 Store[jx][jy] = EL_ACID;
13511 ContinueMoving(jx, jy);
13512 BuryPlayer(player);
13515 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13521 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13522 if (can_move != MP_MOVING)
13525 /* check if DigField() has caused relocation of the player */
13526 if (player->jx != jx || player->jy != jy)
13527 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13529 StorePlayer[jx][jy] = 0;
13530 player->last_jx = jx;
13531 player->last_jy = jy;
13532 player->jx = new_jx;
13533 player->jy = new_jy;
13534 StorePlayer[new_jx][new_jy] = player->element_nr;
13536 if (player->move_delay_value_next != -1)
13538 player->move_delay_value = player->move_delay_value_next;
13539 player->move_delay_value_next = -1;
13543 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13545 player->step_counter++;
13547 PlayerVisit[jx][jy] = FrameCounter;
13549 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13550 player->is_moving = TRUE;
13554 /* should better be called in MovePlayer(), but this breaks some tapes */
13555 ScrollPlayer(player, SCROLL_INIT);
13561 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13563 int jx = player->jx, jy = player->jy;
13564 int old_jx = jx, old_jy = jy;
13565 int moved = MP_NO_ACTION;
13567 if (!player->active)
13572 if (player->MovPos == 0)
13574 player->is_moving = FALSE;
13575 player->is_digging = FALSE;
13576 player->is_collecting = FALSE;
13577 player->is_snapping = FALSE;
13578 player->is_pushing = FALSE;
13584 if (player->move_delay > 0)
13587 player->move_delay = -1; /* set to "uninitialized" value */
13589 /* store if player is automatically moved to next field */
13590 player->is_auto_moving = (player->programmed_action != MV_NONE);
13592 /* remove the last programmed player action */
13593 player->programmed_action = 0;
13595 if (player->MovPos)
13597 /* should only happen if pre-1.2 tape recordings are played */
13598 /* this is only for backward compatibility */
13600 int original_move_delay_value = player->move_delay_value;
13603 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13607 /* scroll remaining steps with finest movement resolution */
13608 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13610 while (player->MovPos)
13612 ScrollPlayer(player, SCROLL_GO_ON);
13613 ScrollScreen(NULL, SCROLL_GO_ON);
13615 AdvanceFrameAndPlayerCounters(player->index_nr);
13621 player->move_delay_value = original_move_delay_value;
13624 player->is_active = FALSE;
13626 if (player->last_move_dir & MV_HORIZONTAL)
13628 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13629 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13633 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13634 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13637 #if USE_FIXED_BORDER_RUNNING_GFX
13638 if (!moved && !player->is_active)
13640 player->is_moving = FALSE;
13641 player->is_digging = FALSE;
13642 player->is_collecting = FALSE;
13643 player->is_snapping = FALSE;
13644 player->is_pushing = FALSE;
13652 if (moved & MP_MOVING && !ScreenMovPos &&
13653 (player->index_nr == game.centered_player_nr ||
13654 game.centered_player_nr == -1))
13656 if (moved & MP_MOVING && !ScreenMovPos &&
13657 (player == local_player || !options.network))
13660 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13661 int offset = game.scroll_delay_value;
13663 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13665 /* actual player has left the screen -- scroll in that direction */
13666 if (jx != old_jx) /* player has moved horizontally */
13667 scroll_x += (jx - old_jx);
13668 else /* player has moved vertically */
13669 scroll_y += (jy - old_jy);
13673 if (jx != old_jx) /* player has moved horizontally */
13675 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13676 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13677 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13679 /* don't scroll over playfield boundaries */
13680 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13681 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13683 /* don't scroll more than one field at a time */
13684 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13686 /* don't scroll against the player's moving direction */
13687 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13688 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13689 scroll_x = old_scroll_x;
13691 else /* player has moved vertically */
13693 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13694 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13695 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13697 /* don't scroll over playfield boundaries */
13698 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13699 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13701 /* don't scroll more than one field at a time */
13702 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13704 /* don't scroll against the player's moving direction */
13705 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13706 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13707 scroll_y = old_scroll_y;
13711 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13714 if (!options.network && game.centered_player_nr == -1 &&
13715 !AllPlayersInVisibleScreen())
13717 scroll_x = old_scroll_x;
13718 scroll_y = old_scroll_y;
13722 if (!options.network && !AllPlayersInVisibleScreen())
13724 scroll_x = old_scroll_x;
13725 scroll_y = old_scroll_y;
13730 ScrollScreen(player, SCROLL_INIT);
13731 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13736 player->StepFrame = 0;
13738 if (moved & MP_MOVING)
13740 if (old_jx != jx && old_jy == jy)
13741 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13742 else if (old_jx == jx && old_jy != jy)
13743 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13745 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13747 player->last_move_dir = player->MovDir;
13748 player->is_moving = TRUE;
13749 player->is_snapping = FALSE;
13750 player->is_switching = FALSE;
13751 player->is_dropping = FALSE;
13752 player->is_dropping_pressed = FALSE;
13753 player->drop_pressed_delay = 0;
13756 /* should better be called here than above, but this breaks some tapes */
13757 ScrollPlayer(player, SCROLL_INIT);
13762 CheckGravityMovementWhenNotMoving(player);
13764 player->is_moving = FALSE;
13766 /* at this point, the player is allowed to move, but cannot move right now
13767 (e.g. because of something blocking the way) -- ensure that the player
13768 is also allowed to move in the next frame (in old versions before 3.1.1,
13769 the player was forced to wait again for eight frames before next try) */
13771 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13772 player->move_delay = 0; /* allow direct movement in the next frame */
13775 if (player->move_delay == -1) /* not yet initialized by DigField() */
13776 player->move_delay = player->move_delay_value;
13778 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13780 TestIfPlayerTouchesBadThing(jx, jy);
13781 TestIfPlayerTouchesCustomElement(jx, jy);
13784 if (!player->active)
13785 RemovePlayer(player);
13790 void ScrollPlayer(struct PlayerInfo *player, int mode)
13792 int jx = player->jx, jy = player->jy;
13793 int last_jx = player->last_jx, last_jy = player->last_jy;
13794 int move_stepsize = TILEX / player->move_delay_value;
13796 #if USE_NEW_PLAYER_SPEED
13797 if (!player->active)
13800 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13803 if (!player->active || player->MovPos == 0)
13807 if (mode == SCROLL_INIT)
13809 player->actual_frame_counter = FrameCounter;
13810 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13812 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13813 Feld[last_jx][last_jy] == EL_EMPTY)
13815 int last_field_block_delay = 0; /* start with no blocking at all */
13816 int block_delay_adjustment = player->block_delay_adjustment;
13818 /* if player blocks last field, add delay for exactly one move */
13819 if (player->block_last_field)
13821 last_field_block_delay += player->move_delay_value;
13823 /* when blocking enabled, prevent moving up despite gravity */
13824 #if USE_PLAYER_GRAVITY
13825 if (player->gravity && player->MovDir == MV_UP)
13826 block_delay_adjustment = -1;
13828 if (game.gravity && player->MovDir == MV_UP)
13829 block_delay_adjustment = -1;
13833 /* add block delay adjustment (also possible when not blocking) */
13834 last_field_block_delay += block_delay_adjustment;
13836 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13837 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13840 #if USE_NEW_PLAYER_SPEED
13841 if (player->MovPos != 0) /* player has not yet reached destination */
13847 else if (!FrameReached(&player->actual_frame_counter, 1))
13850 #if USE_NEW_PLAYER_SPEED
13851 if (player->MovPos != 0)
13853 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13854 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13856 /* before DrawPlayer() to draw correct player graphic for this case */
13857 if (player->MovPos == 0)
13858 CheckGravityMovement(player);
13861 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13862 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13864 /* before DrawPlayer() to draw correct player graphic for this case */
13865 if (player->MovPos == 0)
13866 CheckGravityMovement(player);
13869 if (player->MovPos == 0) /* player reached destination field */
13871 if (player->move_delay_reset_counter > 0)
13873 player->move_delay_reset_counter--;
13875 if (player->move_delay_reset_counter == 0)
13877 /* continue with normal speed after quickly moving through gate */
13878 HALVE_PLAYER_SPEED(player);
13880 /* be able to make the next move without delay */
13881 player->move_delay = 0;
13885 player->last_jx = jx;
13886 player->last_jy = jy;
13888 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13889 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13891 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13893 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13894 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13896 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13898 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13899 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13901 DrawPlayer(player); /* needed here only to cleanup last field */
13902 RemovePlayer(player);
13904 if (local_player->friends_still_needed == 0 ||
13905 IS_SP_ELEMENT(Feld[jx][jy]))
13906 PlayerWins(player);
13909 /* this breaks one level: "machine", level 000 */
13911 int move_direction = player->MovDir;
13912 int enter_side = MV_DIR_OPPOSITE(move_direction);
13913 int leave_side = move_direction;
13914 int old_jx = last_jx;
13915 int old_jy = last_jy;
13916 int old_element = Feld[old_jx][old_jy];
13917 int new_element = Feld[jx][jy];
13919 if (IS_CUSTOM_ELEMENT(old_element))
13920 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13922 player->index_bit, leave_side);
13924 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13925 CE_PLAYER_LEAVES_X,
13926 player->index_bit, leave_side);
13928 if (IS_CUSTOM_ELEMENT(new_element))
13929 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13930 player->index_bit, enter_side);
13932 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13933 CE_PLAYER_ENTERS_X,
13934 player->index_bit, enter_side);
13936 #if USE_FIX_CE_ACTION_WITH_PLAYER
13937 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13938 CE_MOVE_OF_X, move_direction);
13940 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13941 CE_MOVE_OF_X, move_direction);
13945 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13947 TestIfPlayerTouchesBadThing(jx, jy);
13948 TestIfPlayerTouchesCustomElement(jx, jy);
13950 /* needed because pushed element has not yet reached its destination,
13951 so it would trigger a change event at its previous field location */
13952 if (!player->is_pushing)
13953 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13955 if (!player->active)
13956 RemovePlayer(player);
13959 if (!local_player->LevelSolved && level.use_step_counter)
13969 if (TimeLeft <= 10 && setup.time_limit)
13970 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13973 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13975 DisplayGameControlValues();
13977 DrawGameValue_Time(TimeLeft);
13980 if (!TimeLeft && setup.time_limit)
13981 for (i = 0; i < MAX_PLAYERS; i++)
13982 KillPlayer(&stored_player[i]);
13985 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13987 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13989 DisplayGameControlValues();
13992 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13993 DrawGameValue_Time(TimePlayed);
13997 if (tape.single_step && tape.recording && !tape.pausing &&
13998 !player->programmed_action)
13999 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14003 void ScrollScreen(struct PlayerInfo *player, int mode)
14005 static unsigned long screen_frame_counter = 0;
14007 if (mode == SCROLL_INIT)
14009 /* set scrolling step size according to actual player's moving speed */
14010 ScrollStepSize = TILEX / player->move_delay_value;
14012 screen_frame_counter = FrameCounter;
14013 ScreenMovDir = player->MovDir;
14014 ScreenMovPos = player->MovPos;
14015 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14018 else if (!FrameReached(&screen_frame_counter, 1))
14023 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14024 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14025 redraw_mask |= REDRAW_FIELD;
14028 ScreenMovDir = MV_NONE;
14031 void TestIfPlayerTouchesCustomElement(int x, int y)
14033 static int xy[4][2] =
14040 static int trigger_sides[4][2] =
14042 /* center side border side */
14043 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14044 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14045 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14046 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14048 static int touch_dir[4] =
14050 MV_LEFT | MV_RIGHT,
14055 int center_element = Feld[x][y]; /* should always be non-moving! */
14058 for (i = 0; i < NUM_DIRECTIONS; i++)
14060 int xx = x + xy[i][0];
14061 int yy = y + xy[i][1];
14062 int center_side = trigger_sides[i][0];
14063 int border_side = trigger_sides[i][1];
14064 int border_element;
14066 if (!IN_LEV_FIELD(xx, yy))
14069 if (IS_PLAYER(x, y)) /* player found at center element */
14071 struct PlayerInfo *player = PLAYERINFO(x, y);
14073 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14074 border_element = Feld[xx][yy]; /* may be moving! */
14075 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14076 border_element = Feld[xx][yy];
14077 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14078 border_element = MovingOrBlocked2Element(xx, yy);
14080 continue; /* center and border element do not touch */
14082 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14083 player->index_bit, border_side);
14084 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14085 CE_PLAYER_TOUCHES_X,
14086 player->index_bit, border_side);
14088 #if USE_FIX_CE_ACTION_WITH_PLAYER
14090 /* use player element that is initially defined in the level playfield,
14091 not the player element that corresponds to the runtime player number
14092 (example: a level that contains EL_PLAYER_3 as the only player would
14093 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14094 int player_element = PLAYERINFO(x, y)->initial_element;
14096 CheckElementChangeBySide(xx, yy, border_element, player_element,
14097 CE_TOUCHING_X, border_side);
14101 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14103 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14105 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14107 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14108 continue; /* center and border element do not touch */
14111 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14112 player->index_bit, center_side);
14113 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14114 CE_PLAYER_TOUCHES_X,
14115 player->index_bit, center_side);
14117 #if USE_FIX_CE_ACTION_WITH_PLAYER
14119 /* use player element that is initially defined in the level playfield,
14120 not the player element that corresponds to the runtime player number
14121 (example: a level that contains EL_PLAYER_3 as the only player would
14122 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14123 int player_element = PLAYERINFO(xx, yy)->initial_element;
14125 CheckElementChangeBySide(x, y, center_element, player_element,
14126 CE_TOUCHING_X, center_side);
14135 #if USE_ELEMENT_TOUCHING_BUGFIX
14137 void TestIfElementTouchesCustomElement(int x, int y)
14139 static int xy[4][2] =
14146 static int trigger_sides[4][2] =
14148 /* center side border side */
14149 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14150 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14151 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14152 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14154 static int touch_dir[4] =
14156 MV_LEFT | MV_RIGHT,
14161 boolean change_center_element = FALSE;
14162 int center_element = Feld[x][y]; /* should always be non-moving! */
14163 int border_element_old[NUM_DIRECTIONS];
14166 for (i = 0; i < NUM_DIRECTIONS; i++)
14168 int xx = x + xy[i][0];
14169 int yy = y + xy[i][1];
14170 int border_element;
14172 border_element_old[i] = -1;
14174 if (!IN_LEV_FIELD(xx, yy))
14177 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14178 border_element = Feld[xx][yy]; /* may be moving! */
14179 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14180 border_element = Feld[xx][yy];
14181 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14182 border_element = MovingOrBlocked2Element(xx, yy);
14184 continue; /* center and border element do not touch */
14186 border_element_old[i] = border_element;
14189 for (i = 0; i < NUM_DIRECTIONS; i++)
14191 int xx = x + xy[i][0];
14192 int yy = y + xy[i][1];
14193 int center_side = trigger_sides[i][0];
14194 int border_element = border_element_old[i];
14196 if (border_element == -1)
14199 /* check for change of border element */
14200 CheckElementChangeBySide(xx, yy, border_element, center_element,
14201 CE_TOUCHING_X, center_side);
14203 /* (center element cannot be player, so we dont have to check this here) */
14206 for (i = 0; i < NUM_DIRECTIONS; i++)
14208 int xx = x + xy[i][0];
14209 int yy = y + xy[i][1];
14210 int border_side = trigger_sides[i][1];
14211 int border_element = border_element_old[i];
14213 if (border_element == -1)
14216 /* check for change of center element (but change it only once) */
14217 if (!change_center_element)
14218 change_center_element =
14219 CheckElementChangeBySide(x, y, center_element, border_element,
14220 CE_TOUCHING_X, border_side);
14222 #if USE_FIX_CE_ACTION_WITH_PLAYER
14223 if (IS_PLAYER(xx, yy))
14225 /* use player element that is initially defined in the level playfield,
14226 not the player element that corresponds to the runtime player number
14227 (example: a level that contains EL_PLAYER_3 as the only player would
14228 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14229 int player_element = PLAYERINFO(xx, yy)->initial_element;
14231 CheckElementChangeBySide(x, y, center_element, player_element,
14232 CE_TOUCHING_X, border_side);
14240 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14242 static int xy[4][2] =
14249 static int trigger_sides[4][2] =
14251 /* center side border side */
14252 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14253 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14254 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14255 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14257 static int touch_dir[4] =
14259 MV_LEFT | MV_RIGHT,
14264 boolean change_center_element = FALSE;
14265 int center_element = Feld[x][y]; /* should always be non-moving! */
14268 for (i = 0; i < NUM_DIRECTIONS; i++)
14270 int xx = x + xy[i][0];
14271 int yy = y + xy[i][1];
14272 int center_side = trigger_sides[i][0];
14273 int border_side = trigger_sides[i][1];
14274 int border_element;
14276 if (!IN_LEV_FIELD(xx, yy))
14279 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14280 border_element = Feld[xx][yy]; /* may be moving! */
14281 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14282 border_element = Feld[xx][yy];
14283 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14284 border_element = MovingOrBlocked2Element(xx, yy);
14286 continue; /* center and border element do not touch */
14288 /* check for change of center element (but change it only once) */
14289 if (!change_center_element)
14290 change_center_element =
14291 CheckElementChangeBySide(x, y, center_element, border_element,
14292 CE_TOUCHING_X, border_side);
14294 /* check for change of border element */
14295 CheckElementChangeBySide(xx, yy, border_element, center_element,
14296 CE_TOUCHING_X, center_side);
14302 void TestIfElementHitsCustomElement(int x, int y, int direction)
14304 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14305 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14306 int hitx = x + dx, hity = y + dy;
14307 int hitting_element = Feld[x][y];
14308 int touched_element;
14310 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14313 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14314 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14316 if (IN_LEV_FIELD(hitx, hity))
14318 int opposite_direction = MV_DIR_OPPOSITE(direction);
14319 int hitting_side = direction;
14320 int touched_side = opposite_direction;
14321 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14322 MovDir[hitx][hity] != direction ||
14323 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14329 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14330 CE_HITTING_X, touched_side);
14332 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14333 CE_HIT_BY_X, hitting_side);
14335 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14336 CE_HIT_BY_SOMETHING, opposite_direction);
14338 #if USE_FIX_CE_ACTION_WITH_PLAYER
14339 if (IS_PLAYER(hitx, hity))
14341 /* use player element that is initially defined in the level playfield,
14342 not the player element that corresponds to the runtime player number
14343 (example: a level that contains EL_PLAYER_3 as the only player would
14344 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14345 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14347 CheckElementChangeBySide(x, y, hitting_element, player_element,
14348 CE_HITTING_X, touched_side);
14354 /* "hitting something" is also true when hitting the playfield border */
14355 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14356 CE_HITTING_SOMETHING, direction);
14360 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14362 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14363 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14364 int hitx = x + dx, hity = y + dy;
14365 int hitting_element = Feld[x][y];
14366 int touched_element;
14368 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14369 !IS_FREE(hitx, hity) &&
14370 (!IS_MOVING(hitx, hity) ||
14371 MovDir[hitx][hity] != direction ||
14372 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14375 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14379 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14383 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14384 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14386 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14387 EP_CAN_SMASH_EVERYTHING, direction);
14389 if (IN_LEV_FIELD(hitx, hity))
14391 int opposite_direction = MV_DIR_OPPOSITE(direction);
14392 int hitting_side = direction;
14393 int touched_side = opposite_direction;
14395 int touched_element = MovingOrBlocked2Element(hitx, hity);
14398 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14399 MovDir[hitx][hity] != direction ||
14400 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14409 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14410 CE_SMASHED_BY_SOMETHING, opposite_direction);
14412 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14413 CE_OTHER_IS_SMASHING, touched_side);
14415 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14416 CE_OTHER_GETS_SMASHED, hitting_side);
14422 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14424 int i, kill_x = -1, kill_y = -1;
14426 int bad_element = -1;
14427 static int test_xy[4][2] =
14434 static int test_dir[4] =
14442 for (i = 0; i < NUM_DIRECTIONS; i++)
14444 int test_x, test_y, test_move_dir, test_element;
14446 test_x = good_x + test_xy[i][0];
14447 test_y = good_y + test_xy[i][1];
14449 if (!IN_LEV_FIELD(test_x, test_y))
14453 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14455 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14457 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14458 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14460 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14461 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14465 bad_element = test_element;
14471 if (kill_x != -1 || kill_y != -1)
14473 if (IS_PLAYER(good_x, good_y))
14475 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14477 if (player->shield_deadly_time_left > 0 &&
14478 !IS_INDESTRUCTIBLE(bad_element))
14479 Bang(kill_x, kill_y);
14480 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14481 KillPlayer(player);
14484 Bang(good_x, good_y);
14488 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14490 int i, kill_x = -1, kill_y = -1;
14491 int bad_element = Feld[bad_x][bad_y];
14492 static int test_xy[4][2] =
14499 static int touch_dir[4] =
14501 MV_LEFT | MV_RIGHT,
14506 static int test_dir[4] =
14514 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14517 for (i = 0; i < NUM_DIRECTIONS; i++)
14519 int test_x, test_y, test_move_dir, test_element;
14521 test_x = bad_x + test_xy[i][0];
14522 test_y = bad_y + test_xy[i][1];
14524 if (!IN_LEV_FIELD(test_x, test_y))
14528 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14530 test_element = Feld[test_x][test_y];
14532 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14533 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14535 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14536 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14538 /* good thing is player or penguin that does not move away */
14539 if (IS_PLAYER(test_x, test_y))
14541 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14543 if (bad_element == EL_ROBOT && player->is_moving)
14544 continue; /* robot does not kill player if he is moving */
14546 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14548 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14549 continue; /* center and border element do not touch */
14557 else if (test_element == EL_PENGUIN)
14567 if (kill_x != -1 || kill_y != -1)
14569 if (IS_PLAYER(kill_x, kill_y))
14571 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14573 if (player->shield_deadly_time_left > 0 &&
14574 !IS_INDESTRUCTIBLE(bad_element))
14575 Bang(bad_x, bad_y);
14576 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14577 KillPlayer(player);
14580 Bang(kill_x, kill_y);
14584 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14586 int bad_element = Feld[bad_x][bad_y];
14587 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14588 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14589 int test_x = bad_x + dx, test_y = bad_y + dy;
14590 int test_move_dir, test_element;
14591 int kill_x = -1, kill_y = -1;
14593 if (!IN_LEV_FIELD(test_x, test_y))
14597 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14599 test_element = Feld[test_x][test_y];
14601 if (test_move_dir != bad_move_dir)
14603 /* good thing can be player or penguin that does not move away */
14604 if (IS_PLAYER(test_x, test_y))
14606 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14608 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14609 player as being hit when he is moving towards the bad thing, because
14610 the "get hit by" condition would be lost after the player stops) */
14611 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14612 return; /* player moves away from bad thing */
14617 else if (test_element == EL_PENGUIN)
14624 if (kill_x != -1 || kill_y != -1)
14626 if (IS_PLAYER(kill_x, kill_y))
14628 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14630 if (player->shield_deadly_time_left > 0 &&
14631 !IS_INDESTRUCTIBLE(bad_element))
14632 Bang(bad_x, bad_y);
14633 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14634 KillPlayer(player);
14637 Bang(kill_x, kill_y);
14641 void TestIfPlayerTouchesBadThing(int x, int y)
14643 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14646 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14648 TestIfGoodThingHitsBadThing(x, y, move_dir);
14651 void TestIfBadThingTouchesPlayer(int x, int y)
14653 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14656 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14658 TestIfBadThingHitsGoodThing(x, y, move_dir);
14661 void TestIfFriendTouchesBadThing(int x, int y)
14663 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14666 void TestIfBadThingTouchesFriend(int x, int y)
14668 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14671 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14673 int i, kill_x = bad_x, kill_y = bad_y;
14674 static int xy[4][2] =
14682 for (i = 0; i < NUM_DIRECTIONS; i++)
14686 x = bad_x + xy[i][0];
14687 y = bad_y + xy[i][1];
14688 if (!IN_LEV_FIELD(x, y))
14691 element = Feld[x][y];
14692 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14693 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14701 if (kill_x != bad_x || kill_y != bad_y)
14702 Bang(bad_x, bad_y);
14705 void KillPlayer(struct PlayerInfo *player)
14707 int jx = player->jx, jy = player->jy;
14709 if (!player->active)
14713 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14714 player->killed, player->active, player->reanimated);
14717 /* the following code was introduced to prevent an infinite loop when calling
14719 -> CheckTriggeredElementChangeExt()
14720 -> ExecuteCustomElementAction()
14722 -> (infinitely repeating the above sequence of function calls)
14723 which occurs when killing the player while having a CE with the setting
14724 "kill player X when explosion of <player X>"; the solution using a new
14725 field "player->killed" was chosen for backwards compatibility, although
14726 clever use of the fields "player->active" etc. would probably also work */
14728 if (player->killed)
14732 player->killed = TRUE;
14734 /* remove accessible field at the player's position */
14735 Feld[jx][jy] = EL_EMPTY;
14737 /* deactivate shield (else Bang()/Explode() would not work right) */
14738 player->shield_normal_time_left = 0;
14739 player->shield_deadly_time_left = 0;
14742 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14743 player->killed, player->active, player->reanimated);
14749 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14750 player->killed, player->active, player->reanimated);
14753 #if USE_PLAYER_REANIMATION
14755 if (player->reanimated) /* killed player may have been reanimated */
14756 player->killed = player->reanimated = FALSE;
14758 BuryPlayer(player);
14760 if (player->killed) /* player may have been reanimated */
14761 BuryPlayer(player);
14764 BuryPlayer(player);
14768 static void KillPlayerUnlessEnemyProtected(int x, int y)
14770 if (!PLAYER_ENEMY_PROTECTED(x, y))
14771 KillPlayer(PLAYERINFO(x, y));
14774 static void KillPlayerUnlessExplosionProtected(int x, int y)
14776 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14777 KillPlayer(PLAYERINFO(x, y));
14780 void BuryPlayer(struct PlayerInfo *player)
14782 int jx = player->jx, jy = player->jy;
14784 if (!player->active)
14787 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14788 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14790 player->GameOver = TRUE;
14791 RemovePlayer(player);
14794 void RemovePlayer(struct PlayerInfo *player)
14796 int jx = player->jx, jy = player->jy;
14797 int i, found = FALSE;
14799 player->present = FALSE;
14800 player->active = FALSE;
14802 if (!ExplodeField[jx][jy])
14803 StorePlayer[jx][jy] = 0;
14805 if (player->is_moving)
14806 TEST_DrawLevelField(player->last_jx, player->last_jy);
14808 for (i = 0; i < MAX_PLAYERS; i++)
14809 if (stored_player[i].active)
14813 AllPlayersGone = TRUE;
14819 #if USE_NEW_SNAP_DELAY
14820 static void setFieldForSnapping(int x, int y, int element, int direction)
14822 struct ElementInfo *ei = &element_info[element];
14823 int direction_bit = MV_DIR_TO_BIT(direction);
14824 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14825 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14826 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14828 Feld[x][y] = EL_ELEMENT_SNAPPING;
14829 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14831 ResetGfxAnimation(x, y);
14833 GfxElement[x][y] = element;
14834 GfxAction[x][y] = action;
14835 GfxDir[x][y] = direction;
14836 GfxFrame[x][y] = -1;
14841 =============================================================================
14842 checkDiagonalPushing()
14843 -----------------------------------------------------------------------------
14844 check if diagonal input device direction results in pushing of object
14845 (by checking if the alternative direction is walkable, diggable, ...)
14846 =============================================================================
14849 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14850 int x, int y, int real_dx, int real_dy)
14852 int jx, jy, dx, dy, xx, yy;
14854 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14857 /* diagonal direction: check alternative direction */
14862 xx = jx + (dx == 0 ? real_dx : 0);
14863 yy = jy + (dy == 0 ? real_dy : 0);
14865 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14869 =============================================================================
14871 -----------------------------------------------------------------------------
14872 x, y: field next to player (non-diagonal) to try to dig to
14873 real_dx, real_dy: direction as read from input device (can be diagonal)
14874 =============================================================================
14877 static int DigField(struct PlayerInfo *player,
14878 int oldx, int oldy, int x, int y,
14879 int real_dx, int real_dy, int mode)
14881 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14882 boolean player_was_pushing = player->is_pushing;
14883 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14884 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14885 int jx = oldx, jy = oldy;
14886 int dx = x - jx, dy = y - jy;
14887 int nextx = x + dx, nexty = y + dy;
14888 int move_direction = (dx == -1 ? MV_LEFT :
14889 dx == +1 ? MV_RIGHT :
14891 dy == +1 ? MV_DOWN : MV_NONE);
14892 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14893 int dig_side = MV_DIR_OPPOSITE(move_direction);
14894 int old_element = Feld[jx][jy];
14895 #if USE_FIXED_DONT_RUN_INTO
14896 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14902 if (is_player) /* function can also be called by EL_PENGUIN */
14904 if (player->MovPos == 0)
14906 player->is_digging = FALSE;
14907 player->is_collecting = FALSE;
14910 if (player->MovPos == 0) /* last pushing move finished */
14911 player->is_pushing = FALSE;
14913 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14915 player->is_switching = FALSE;
14916 player->push_delay = -1;
14918 return MP_NO_ACTION;
14922 #if !USE_FIXED_DONT_RUN_INTO
14923 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14924 return MP_NO_ACTION;
14927 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14928 old_element = Back[jx][jy];
14930 /* in case of element dropped at player position, check background */
14931 else if (Back[jx][jy] != EL_EMPTY &&
14932 game.engine_version >= VERSION_IDENT(2,2,0,0))
14933 old_element = Back[jx][jy];
14935 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14936 return MP_NO_ACTION; /* field has no opening in this direction */
14938 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14939 return MP_NO_ACTION; /* field has no opening in this direction */
14941 #if USE_FIXED_DONT_RUN_INTO
14942 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14946 Feld[jx][jy] = player->artwork_element;
14947 InitMovingField(jx, jy, MV_DOWN);
14948 Store[jx][jy] = EL_ACID;
14949 ContinueMoving(jx, jy);
14950 BuryPlayer(player);
14952 return MP_DONT_RUN_INTO;
14956 #if USE_FIXED_DONT_RUN_INTO
14957 if (player_can_move && DONT_RUN_INTO(element))
14959 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14961 return MP_DONT_RUN_INTO;
14965 #if USE_FIXED_DONT_RUN_INTO
14966 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14967 return MP_NO_ACTION;
14970 #if !USE_FIXED_DONT_RUN_INTO
14971 element = Feld[x][y];
14974 collect_count = element_info[element].collect_count_initial;
14976 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14977 return MP_NO_ACTION;
14979 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14980 player_can_move = player_can_move_or_snap;
14982 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14983 game.engine_version >= VERSION_IDENT(2,2,0,0))
14985 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14986 player->index_bit, dig_side);
14987 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14988 player->index_bit, dig_side);
14990 if (element == EL_DC_LANDMINE)
14993 if (Feld[x][y] != element) /* field changed by snapping */
14996 return MP_NO_ACTION;
14999 #if USE_PLAYER_GRAVITY
15000 if (player->gravity && is_player && !player->is_auto_moving &&
15001 canFallDown(player) && move_direction != MV_DOWN &&
15002 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15003 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15005 if (game.gravity && is_player && !player->is_auto_moving &&
15006 canFallDown(player) && move_direction != MV_DOWN &&
15007 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15008 return MP_NO_ACTION; /* player cannot walk here due to gravity */
15011 if (player_can_move &&
15012 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15014 int sound_element = SND_ELEMENT(element);
15015 int sound_action = ACTION_WALKING;
15017 if (IS_RND_GATE(element))
15019 if (!player->key[RND_GATE_NR(element)])
15020 return MP_NO_ACTION;
15022 else if (IS_RND_GATE_GRAY(element))
15024 if (!player->key[RND_GATE_GRAY_NR(element)])
15025 return MP_NO_ACTION;
15027 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15029 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15030 return MP_NO_ACTION;
15032 else if (element == EL_EXIT_OPEN ||
15033 element == EL_EM_EXIT_OPEN ||
15035 element == EL_EM_EXIT_OPENING ||
15037 element == EL_STEEL_EXIT_OPEN ||
15038 element == EL_EM_STEEL_EXIT_OPEN ||
15040 element == EL_EM_STEEL_EXIT_OPENING ||
15042 element == EL_SP_EXIT_OPEN ||
15043 element == EL_SP_EXIT_OPENING)
15045 sound_action = ACTION_PASSING; /* player is passing exit */
15047 else if (element == EL_EMPTY)
15049 sound_action = ACTION_MOVING; /* nothing to walk on */
15052 /* play sound from background or player, whatever is available */
15053 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15054 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15056 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15058 else if (player_can_move &&
15059 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15061 if (!ACCESS_FROM(element, opposite_direction))
15062 return MP_NO_ACTION; /* field not accessible from this direction */
15064 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15065 return MP_NO_ACTION;
15067 if (IS_EM_GATE(element))
15069 if (!player->key[EM_GATE_NR(element)])
15070 return MP_NO_ACTION;
15072 else if (IS_EM_GATE_GRAY(element))
15074 if (!player->key[EM_GATE_GRAY_NR(element)])
15075 return MP_NO_ACTION;
15077 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15079 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15080 return MP_NO_ACTION;
15082 else if (IS_EMC_GATE(element))
15084 if (!player->key[EMC_GATE_NR(element)])
15085 return MP_NO_ACTION;
15087 else if (IS_EMC_GATE_GRAY(element))
15089 if (!player->key[EMC_GATE_GRAY_NR(element)])
15090 return MP_NO_ACTION;
15092 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15094 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15095 return MP_NO_ACTION;
15097 else if (element == EL_DC_GATE_WHITE ||
15098 element == EL_DC_GATE_WHITE_GRAY ||
15099 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15101 if (player->num_white_keys == 0)
15102 return MP_NO_ACTION;
15104 player->num_white_keys--;
15106 else if (IS_SP_PORT(element))
15108 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15109 element == EL_SP_GRAVITY_PORT_RIGHT ||
15110 element == EL_SP_GRAVITY_PORT_UP ||
15111 element == EL_SP_GRAVITY_PORT_DOWN)
15112 #if USE_PLAYER_GRAVITY
15113 player->gravity = !player->gravity;
15115 game.gravity = !game.gravity;
15117 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15118 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15119 element == EL_SP_GRAVITY_ON_PORT_UP ||
15120 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15121 #if USE_PLAYER_GRAVITY
15122 player->gravity = TRUE;
15124 game.gravity = TRUE;
15126 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15127 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15128 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15129 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15130 #if USE_PLAYER_GRAVITY
15131 player->gravity = FALSE;
15133 game.gravity = FALSE;
15137 /* automatically move to the next field with double speed */
15138 player->programmed_action = move_direction;
15140 if (player->move_delay_reset_counter == 0)
15142 player->move_delay_reset_counter = 2; /* two double speed steps */
15144 DOUBLE_PLAYER_SPEED(player);
15147 PlayLevelSoundAction(x, y, ACTION_PASSING);
15149 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15153 if (mode != DF_SNAP)
15155 GfxElement[x][y] = GFX_ELEMENT(element);
15156 player->is_digging = TRUE;
15159 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15161 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15162 player->index_bit, dig_side);
15164 if (mode == DF_SNAP)
15166 #if USE_NEW_SNAP_DELAY
15167 if (level.block_snap_field)
15168 setFieldForSnapping(x, y, element, move_direction);
15170 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15172 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15175 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15176 player->index_bit, dig_side);
15179 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15183 if (is_player && mode != DF_SNAP)
15185 GfxElement[x][y] = element;
15186 player->is_collecting = TRUE;
15189 if (element == EL_SPEED_PILL)
15191 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15193 else if (element == EL_EXTRA_TIME && level.time > 0)
15195 TimeLeft += level.extra_time;
15198 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15200 DisplayGameControlValues();
15202 DrawGameValue_Time(TimeLeft);
15205 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15207 player->shield_normal_time_left += level.shield_normal_time;
15208 if (element == EL_SHIELD_DEADLY)
15209 player->shield_deadly_time_left += level.shield_deadly_time;
15211 else if (element == EL_DYNAMITE ||
15212 element == EL_EM_DYNAMITE ||
15213 element == EL_SP_DISK_RED)
15215 if (player->inventory_size < MAX_INVENTORY_SIZE)
15216 player->inventory_element[player->inventory_size++] = element;
15218 DrawGameDoorValues();
15220 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15222 player->dynabomb_count++;
15223 player->dynabombs_left++;
15225 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15227 player->dynabomb_size++;
15229 else if (element == EL_DYNABOMB_INCREASE_POWER)
15231 player->dynabomb_xl = TRUE;
15233 else if (IS_KEY(element))
15235 player->key[KEY_NR(element)] = TRUE;
15237 DrawGameDoorValues();
15239 else if (element == EL_DC_KEY_WHITE)
15241 player->num_white_keys++;
15243 /* display white keys? */
15244 /* DrawGameDoorValues(); */
15246 else if (IS_ENVELOPE(element))
15248 player->show_envelope = element;
15250 else if (element == EL_EMC_LENSES)
15252 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15254 RedrawAllInvisibleElementsForLenses();
15256 else if (element == EL_EMC_MAGNIFIER)
15258 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15260 RedrawAllInvisibleElementsForMagnifier();
15262 else if (IS_DROPPABLE(element) ||
15263 IS_THROWABLE(element)) /* can be collected and dropped */
15267 if (collect_count == 0)
15268 player->inventory_infinite_element = element;
15270 for (i = 0; i < collect_count; i++)
15271 if (player->inventory_size < MAX_INVENTORY_SIZE)
15272 player->inventory_element[player->inventory_size++] = element;
15274 DrawGameDoorValues();
15276 else if (collect_count > 0)
15278 local_player->gems_still_needed -= collect_count;
15279 if (local_player->gems_still_needed < 0)
15280 local_player->gems_still_needed = 0;
15283 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15285 DisplayGameControlValues();
15287 DrawGameValue_Emeralds(local_player->gems_still_needed);
15291 RaiseScoreElement(element);
15292 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15295 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15296 player->index_bit, dig_side);
15298 if (mode == DF_SNAP)
15300 #if USE_NEW_SNAP_DELAY
15301 if (level.block_snap_field)
15302 setFieldForSnapping(x, y, element, move_direction);
15304 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15306 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15309 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15310 player->index_bit, dig_side);
15313 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15315 if (mode == DF_SNAP && element != EL_BD_ROCK)
15316 return MP_NO_ACTION;
15318 if (CAN_FALL(element) && dy)
15319 return MP_NO_ACTION;
15321 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15322 !(element == EL_SPRING && level.use_spring_bug))
15323 return MP_NO_ACTION;
15325 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15326 ((move_direction & MV_VERTICAL &&
15327 ((element_info[element].move_pattern & MV_LEFT &&
15328 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15329 (element_info[element].move_pattern & MV_RIGHT &&
15330 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15331 (move_direction & MV_HORIZONTAL &&
15332 ((element_info[element].move_pattern & MV_UP &&
15333 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15334 (element_info[element].move_pattern & MV_DOWN &&
15335 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15336 return MP_NO_ACTION;
15338 /* do not push elements already moving away faster than player */
15339 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15340 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15341 return MP_NO_ACTION;
15343 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15345 if (player->push_delay_value == -1 || !player_was_pushing)
15346 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15348 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15350 if (player->push_delay_value == -1)
15351 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15353 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15355 if (!player->is_pushing)
15356 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15359 player->is_pushing = TRUE;
15360 player->is_active = TRUE;
15362 if (!(IN_LEV_FIELD(nextx, nexty) &&
15363 (IS_FREE(nextx, nexty) ||
15364 (IS_SB_ELEMENT(element) &&
15365 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15366 (IS_CUSTOM_ELEMENT(element) &&
15367 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15368 return MP_NO_ACTION;
15370 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15371 return MP_NO_ACTION;
15373 if (player->push_delay == -1) /* new pushing; restart delay */
15374 player->push_delay = 0;
15376 if (player->push_delay < player->push_delay_value &&
15377 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15378 element != EL_SPRING && element != EL_BALLOON)
15380 /* make sure that there is no move delay before next try to push */
15381 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15382 player->move_delay = 0;
15384 return MP_NO_ACTION;
15387 if (IS_CUSTOM_ELEMENT(element) &&
15388 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15390 if (!DigFieldByCE(nextx, nexty, element))
15391 return MP_NO_ACTION;
15394 if (IS_SB_ELEMENT(element))
15396 if (element == EL_SOKOBAN_FIELD_FULL)
15398 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15399 local_player->sokobanfields_still_needed++;
15402 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15404 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15405 local_player->sokobanfields_still_needed--;
15408 Feld[x][y] = EL_SOKOBAN_OBJECT;
15410 if (Back[x][y] == Back[nextx][nexty])
15411 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15412 else if (Back[x][y] != 0)
15413 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15416 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15420 if (local_player->sokobanfields_still_needed == 0 &&
15421 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15423 if (local_player->sokobanfields_still_needed == 0 &&
15424 game.emulation == EMU_SOKOBAN)
15427 PlayerWins(player);
15429 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15433 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15435 InitMovingField(x, y, move_direction);
15436 GfxAction[x][y] = ACTION_PUSHING;
15438 if (mode == DF_SNAP)
15439 ContinueMoving(x, y);
15441 MovPos[x][y] = (dx != 0 ? dx : dy);
15443 Pushed[x][y] = TRUE;
15444 Pushed[nextx][nexty] = TRUE;
15446 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15447 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15449 player->push_delay_value = -1; /* get new value later */
15451 /* check for element change _after_ element has been pushed */
15452 if (game.use_change_when_pushing_bug)
15454 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15455 player->index_bit, dig_side);
15456 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15457 player->index_bit, dig_side);
15460 else if (IS_SWITCHABLE(element))
15462 if (PLAYER_SWITCHING(player, x, y))
15464 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15465 player->index_bit, dig_side);
15470 player->is_switching = TRUE;
15471 player->switch_x = x;
15472 player->switch_y = y;
15474 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15476 if (element == EL_ROBOT_WHEEL)
15478 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15482 game.robot_wheel_active = TRUE;
15484 TEST_DrawLevelField(x, y);
15486 else if (element == EL_SP_TERMINAL)
15490 SCAN_PLAYFIELD(xx, yy)
15492 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15494 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15495 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15498 else if (IS_BELT_SWITCH(element))
15500 ToggleBeltSwitch(x, y);
15502 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15503 element == EL_SWITCHGATE_SWITCH_DOWN ||
15504 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15505 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15507 ToggleSwitchgateSwitch(x, y);
15509 else if (element == EL_LIGHT_SWITCH ||
15510 element == EL_LIGHT_SWITCH_ACTIVE)
15512 ToggleLightSwitch(x, y);
15514 else if (element == EL_TIMEGATE_SWITCH ||
15515 element == EL_DC_TIMEGATE_SWITCH)
15517 ActivateTimegateSwitch(x, y);
15519 else if (element == EL_BALLOON_SWITCH_LEFT ||
15520 element == EL_BALLOON_SWITCH_RIGHT ||
15521 element == EL_BALLOON_SWITCH_UP ||
15522 element == EL_BALLOON_SWITCH_DOWN ||
15523 element == EL_BALLOON_SWITCH_NONE ||
15524 element == EL_BALLOON_SWITCH_ANY)
15526 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15527 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15528 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15529 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15530 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15533 else if (element == EL_LAMP)
15535 Feld[x][y] = EL_LAMP_ACTIVE;
15536 local_player->lights_still_needed--;
15538 ResetGfxAnimation(x, y);
15539 TEST_DrawLevelField(x, y);
15541 else if (element == EL_TIME_ORB_FULL)
15543 Feld[x][y] = EL_TIME_ORB_EMPTY;
15545 if (level.time > 0 || level.use_time_orb_bug)
15547 TimeLeft += level.time_orb_time;
15550 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15552 DisplayGameControlValues();
15554 DrawGameValue_Time(TimeLeft);
15558 ResetGfxAnimation(x, y);
15559 TEST_DrawLevelField(x, y);
15561 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15562 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15566 game.ball_state = !game.ball_state;
15568 SCAN_PLAYFIELD(xx, yy)
15570 int e = Feld[xx][yy];
15572 if (game.ball_state)
15574 if (e == EL_EMC_MAGIC_BALL)
15575 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15576 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15577 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15581 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15582 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15583 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15584 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15589 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15590 player->index_bit, dig_side);
15592 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15593 player->index_bit, dig_side);
15595 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15596 player->index_bit, dig_side);
15602 if (!PLAYER_SWITCHING(player, x, y))
15604 player->is_switching = TRUE;
15605 player->switch_x = x;
15606 player->switch_y = y;
15608 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15609 player->index_bit, dig_side);
15610 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15611 player->index_bit, dig_side);
15613 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15614 player->index_bit, dig_side);
15615 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15616 player->index_bit, dig_side);
15619 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15620 player->index_bit, dig_side);
15621 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15622 player->index_bit, dig_side);
15624 return MP_NO_ACTION;
15627 player->push_delay = -1;
15629 if (is_player) /* function can also be called by EL_PENGUIN */
15631 if (Feld[x][y] != element) /* really digged/collected something */
15633 player->is_collecting = !player->is_digging;
15634 player->is_active = TRUE;
15641 static boolean DigFieldByCE(int x, int y, int digging_element)
15643 int element = Feld[x][y];
15645 if (!IS_FREE(x, y))
15647 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15648 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15651 /* no element can dig solid indestructible elements */
15652 if (IS_INDESTRUCTIBLE(element) &&
15653 !IS_DIGGABLE(element) &&
15654 !IS_COLLECTIBLE(element))
15657 if (AmoebaNr[x][y] &&
15658 (element == EL_AMOEBA_FULL ||
15659 element == EL_BD_AMOEBA ||
15660 element == EL_AMOEBA_GROWING))
15662 AmoebaCnt[AmoebaNr[x][y]]--;
15663 AmoebaCnt2[AmoebaNr[x][y]]--;
15666 if (IS_MOVING(x, y))
15667 RemoveMovingField(x, y);
15671 TEST_DrawLevelField(x, y);
15674 /* if digged element was about to explode, prevent the explosion */
15675 ExplodeField[x][y] = EX_TYPE_NONE;
15677 PlayLevelSoundAction(x, y, action);
15680 Store[x][y] = EL_EMPTY;
15683 /* this makes it possible to leave the removed element again */
15684 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15685 Store[x][y] = element;
15687 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15689 int move_leave_element = element_info[digging_element].move_leave_element;
15691 /* this makes it possible to leave the removed element again */
15692 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15693 element : move_leave_element);
15700 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15702 int jx = player->jx, jy = player->jy;
15703 int x = jx + dx, y = jy + dy;
15704 int snap_direction = (dx == -1 ? MV_LEFT :
15705 dx == +1 ? MV_RIGHT :
15707 dy == +1 ? MV_DOWN : MV_NONE);
15708 boolean can_continue_snapping = (level.continuous_snapping &&
15709 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15711 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15714 if (!player->active || !IN_LEV_FIELD(x, y))
15722 if (player->MovPos == 0)
15723 player->is_pushing = FALSE;
15725 player->is_snapping = FALSE;
15727 if (player->MovPos == 0)
15729 player->is_moving = FALSE;
15730 player->is_digging = FALSE;
15731 player->is_collecting = FALSE;
15737 #if USE_NEW_CONTINUOUS_SNAPPING
15738 /* prevent snapping with already pressed snap key when not allowed */
15739 if (player->is_snapping && !can_continue_snapping)
15742 if (player->is_snapping)
15746 player->MovDir = snap_direction;
15748 if (player->MovPos == 0)
15750 player->is_moving = FALSE;
15751 player->is_digging = FALSE;
15752 player->is_collecting = FALSE;
15755 player->is_dropping = FALSE;
15756 player->is_dropping_pressed = FALSE;
15757 player->drop_pressed_delay = 0;
15759 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15762 player->is_snapping = TRUE;
15763 player->is_active = TRUE;
15765 if (player->MovPos == 0)
15767 player->is_moving = FALSE;
15768 player->is_digging = FALSE;
15769 player->is_collecting = FALSE;
15772 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15773 TEST_DrawLevelField(player->last_jx, player->last_jy);
15775 TEST_DrawLevelField(x, y);
15780 static boolean DropElement(struct PlayerInfo *player)
15782 int old_element, new_element;
15783 int dropx = player->jx, dropy = player->jy;
15784 int drop_direction = player->MovDir;
15785 int drop_side = drop_direction;
15787 int drop_element = get_next_dropped_element(player);
15789 int drop_element = (player->inventory_size > 0 ?
15790 player->inventory_element[player->inventory_size - 1] :
15791 player->inventory_infinite_element != EL_UNDEFINED ?
15792 player->inventory_infinite_element :
15793 player->dynabombs_left > 0 ?
15794 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15798 player->is_dropping_pressed = TRUE;
15800 /* do not drop an element on top of another element; when holding drop key
15801 pressed without moving, dropped element must move away before the next
15802 element can be dropped (this is especially important if the next element
15803 is dynamite, which can be placed on background for historical reasons) */
15804 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15807 if (IS_THROWABLE(drop_element))
15809 dropx += GET_DX_FROM_DIR(drop_direction);
15810 dropy += GET_DY_FROM_DIR(drop_direction);
15812 if (!IN_LEV_FIELD(dropx, dropy))
15816 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15817 new_element = drop_element; /* default: no change when dropping */
15819 /* check if player is active, not moving and ready to drop */
15820 if (!player->active || player->MovPos || player->drop_delay > 0)
15823 /* check if player has anything that can be dropped */
15824 if (new_element == EL_UNDEFINED)
15827 /* check if drop key was pressed long enough for EM style dynamite */
15828 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15831 /* check if anything can be dropped at the current position */
15832 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15835 /* collected custom elements can only be dropped on empty fields */
15836 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15839 if (old_element != EL_EMPTY)
15840 Back[dropx][dropy] = old_element; /* store old element on this field */
15842 ResetGfxAnimation(dropx, dropy);
15843 ResetRandomAnimationValue(dropx, dropy);
15845 if (player->inventory_size > 0 ||
15846 player->inventory_infinite_element != EL_UNDEFINED)
15848 if (player->inventory_size > 0)
15850 player->inventory_size--;
15852 DrawGameDoorValues();
15854 if (new_element == EL_DYNAMITE)
15855 new_element = EL_DYNAMITE_ACTIVE;
15856 else if (new_element == EL_EM_DYNAMITE)
15857 new_element = EL_EM_DYNAMITE_ACTIVE;
15858 else if (new_element == EL_SP_DISK_RED)
15859 new_element = EL_SP_DISK_RED_ACTIVE;
15862 Feld[dropx][dropy] = new_element;
15864 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15865 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15866 el2img(Feld[dropx][dropy]), 0);
15868 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15870 /* needed if previous element just changed to "empty" in the last frame */
15871 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15873 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15874 player->index_bit, drop_side);
15875 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15877 player->index_bit, drop_side);
15879 TestIfElementTouchesCustomElement(dropx, dropy);
15881 else /* player is dropping a dyna bomb */
15883 player->dynabombs_left--;
15885 Feld[dropx][dropy] = new_element;
15887 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15888 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15889 el2img(Feld[dropx][dropy]), 0);
15891 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15894 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15895 InitField_WithBug1(dropx, dropy, FALSE);
15897 new_element = Feld[dropx][dropy]; /* element might have changed */
15899 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15900 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15902 int move_direction, nextx, nexty;
15904 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15905 MovDir[dropx][dropy] = drop_direction;
15907 move_direction = MovDir[dropx][dropy];
15908 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15909 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15911 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15913 #if USE_FIX_IMPACT_COLLISION
15914 /* do not cause impact style collision by dropping elements that can fall */
15915 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15917 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15921 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15922 player->is_dropping = TRUE;
15924 player->drop_pressed_delay = 0;
15925 player->is_dropping_pressed = FALSE;
15927 player->drop_x = dropx;
15928 player->drop_y = dropy;
15933 /* ------------------------------------------------------------------------- */
15934 /* game sound playing functions */
15935 /* ------------------------------------------------------------------------- */
15937 static int *loop_sound_frame = NULL;
15938 static int *loop_sound_volume = NULL;
15940 void InitPlayLevelSound()
15942 int num_sounds = getSoundListSize();
15944 checked_free(loop_sound_frame);
15945 checked_free(loop_sound_volume);
15947 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15948 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15951 static void PlayLevelSound(int x, int y, int nr)
15953 int sx = SCREENX(x), sy = SCREENY(y);
15954 int volume, stereo_position;
15955 int max_distance = 8;
15956 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15958 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15959 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15962 if (!IN_LEV_FIELD(x, y) ||
15963 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15964 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15967 volume = SOUND_MAX_VOLUME;
15969 if (!IN_SCR_FIELD(sx, sy))
15971 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15972 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15974 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15977 stereo_position = (SOUND_MAX_LEFT +
15978 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15979 (SCR_FIELDX + 2 * max_distance));
15981 if (IS_LOOP_SOUND(nr))
15983 /* This assures that quieter loop sounds do not overwrite louder ones,
15984 while restarting sound volume comparison with each new game frame. */
15986 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15989 loop_sound_volume[nr] = volume;
15990 loop_sound_frame[nr] = FrameCounter;
15993 PlaySoundExt(nr, volume, stereo_position, type);
15996 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15998 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15999 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16000 y < LEVELY(BY1) ? LEVELY(BY1) :
16001 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16005 static void PlayLevelSoundAction(int x, int y, int action)
16007 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16010 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16012 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16014 if (sound_effect != SND_UNDEFINED)
16015 PlayLevelSound(x, y, sound_effect);
16018 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16021 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16023 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16024 PlayLevelSound(x, y, sound_effect);
16027 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16029 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16031 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16032 PlayLevelSound(x, y, sound_effect);
16035 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16037 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16039 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16040 StopSound(sound_effect);
16043 static void PlayLevelMusic()
16045 if (levelset.music[level_nr] != MUS_UNDEFINED)
16046 PlayMusic(levelset.music[level_nr]); /* from config file */
16048 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16051 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16053 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16054 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16055 int x = xx - 1 - offset;
16056 int y = yy - 1 - offset;
16061 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16065 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16069 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16073 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16077 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16081 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16085 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16088 case SAMPLE_android_clone:
16089 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16092 case SAMPLE_android_move:
16093 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16096 case SAMPLE_spring:
16097 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16101 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16105 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16108 case SAMPLE_eater_eat:
16109 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16116 case SAMPLE_collect:
16117 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16120 case SAMPLE_diamond:
16121 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16124 case SAMPLE_squash:
16125 /* !!! CHECK THIS !!! */
16127 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16129 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16133 case SAMPLE_wonderfall:
16134 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16138 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16142 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16146 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16150 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16154 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16158 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16161 case SAMPLE_wonder:
16162 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16166 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16169 case SAMPLE_exit_open:
16170 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16173 case SAMPLE_exit_leave:
16174 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16177 case SAMPLE_dynamite:
16178 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16182 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16186 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16190 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16194 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16198 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16202 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16206 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16212 void ChangeTime(int value)
16214 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16218 /* EMC game engine uses value from time counter of RND game engine */
16219 level.native_em_level->lev->time = *time;
16221 DrawGameValue_Time(*time);
16224 void RaiseScore(int value)
16226 /* EMC game engine and RND game engine have separate score counters */
16227 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16228 &level.native_em_level->lev->score : &local_player->score);
16232 DrawGameValue_Score(*score);
16236 void RaiseScore(int value)
16238 local_player->score += value;
16241 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16243 DisplayGameControlValues();
16245 DrawGameValue_Score(local_player->score);
16249 void RaiseScoreElement(int element)
16254 case EL_BD_DIAMOND:
16255 case EL_EMERALD_YELLOW:
16256 case EL_EMERALD_RED:
16257 case EL_EMERALD_PURPLE:
16258 case EL_SP_INFOTRON:
16259 RaiseScore(level.score[SC_EMERALD]);
16262 RaiseScore(level.score[SC_DIAMOND]);
16265 RaiseScore(level.score[SC_CRYSTAL]);
16268 RaiseScore(level.score[SC_PEARL]);
16271 case EL_BD_BUTTERFLY:
16272 case EL_SP_ELECTRON:
16273 RaiseScore(level.score[SC_BUG]);
16276 case EL_BD_FIREFLY:
16277 case EL_SP_SNIKSNAK:
16278 RaiseScore(level.score[SC_SPACESHIP]);
16281 case EL_DARK_YAMYAM:
16282 RaiseScore(level.score[SC_YAMYAM]);
16285 RaiseScore(level.score[SC_ROBOT]);
16288 RaiseScore(level.score[SC_PACMAN]);
16291 RaiseScore(level.score[SC_NUT]);
16294 case EL_EM_DYNAMITE:
16295 case EL_SP_DISK_RED:
16296 case EL_DYNABOMB_INCREASE_NUMBER:
16297 case EL_DYNABOMB_INCREASE_SIZE:
16298 case EL_DYNABOMB_INCREASE_POWER:
16299 RaiseScore(level.score[SC_DYNAMITE]);
16301 case EL_SHIELD_NORMAL:
16302 case EL_SHIELD_DEADLY:
16303 RaiseScore(level.score[SC_SHIELD]);
16305 case EL_EXTRA_TIME:
16306 RaiseScore(level.extra_time_score);
16320 case EL_DC_KEY_WHITE:
16321 RaiseScore(level.score[SC_KEY]);
16324 RaiseScore(element_info[element].collect_score);
16329 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16331 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16333 #if defined(NETWORK_AVALIABLE)
16334 if (options.network)
16335 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16344 FadeSkipNextFadeIn();
16346 fading = fading_none;
16350 OpenDoor(DOOR_CLOSE_1);
16353 game_status = GAME_MODE_MAIN;
16356 DrawAndFadeInMainMenu(REDRAW_FIELD);
16364 FadeOut(REDRAW_FIELD);
16367 game_status = GAME_MODE_MAIN;
16369 DrawAndFadeInMainMenu(REDRAW_FIELD);
16373 else /* continue playing the game */
16375 if (tape.playing && tape.deactivate_display)
16376 TapeDeactivateDisplayOff(TRUE);
16378 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16380 if (tape.playing && tape.deactivate_display)
16381 TapeDeactivateDisplayOn();
16385 void RequestQuitGame(boolean ask_if_really_quit)
16387 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16388 boolean skip_request = AllPlayersGone || quick_quit;
16390 RequestQuitGameExt(skip_request, quick_quit,
16391 "Do you really want to quit the game ?");
16395 /* ------------------------------------------------------------------------- */
16396 /* random generator functions */
16397 /* ------------------------------------------------------------------------- */
16399 unsigned int InitEngineRandom_RND(long seed)
16401 game.num_random_calls = 0;
16404 unsigned int rnd_seed = InitEngineRandom(seed);
16406 printf("::: START RND: %d\n", rnd_seed);
16411 return InitEngineRandom(seed);
16417 unsigned int RND(int max)
16421 game.num_random_calls++;
16423 return GetEngineRandom(max);
16430 /* ------------------------------------------------------------------------- */
16431 /* game engine snapshot handling functions */
16432 /* ------------------------------------------------------------------------- */
16434 struct EngineSnapshotInfo
16436 /* runtime values for custom element collect score */
16437 int collect_score[NUM_CUSTOM_ELEMENTS];
16439 /* runtime values for group element choice position */
16440 int choice_pos[NUM_GROUP_ELEMENTS];
16442 /* runtime values for belt position animations */
16443 int belt_graphic[4 * NUM_BELT_PARTS];
16444 int belt_anim_mode[4 * NUM_BELT_PARTS];
16447 static struct EngineSnapshotInfo engine_snapshot_rnd;
16448 static char *snapshot_level_identifier = NULL;
16449 static int snapshot_level_nr = -1;
16451 static void SaveEngineSnapshotValues_RND()
16453 static int belt_base_active_element[4] =
16455 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16456 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16457 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16458 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16462 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16464 int element = EL_CUSTOM_START + i;
16466 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16469 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16471 int element = EL_GROUP_START + i;
16473 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16476 for (i = 0; i < 4; i++)
16478 for (j = 0; j < NUM_BELT_PARTS; j++)
16480 int element = belt_base_active_element[i] + j;
16481 int graphic = el2img(element);
16482 int anim_mode = graphic_info[graphic].anim_mode;
16484 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16485 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16490 static void LoadEngineSnapshotValues_RND()
16492 unsigned long num_random_calls = game.num_random_calls;
16495 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16497 int element = EL_CUSTOM_START + i;
16499 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16502 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16504 int element = EL_GROUP_START + i;
16506 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16509 for (i = 0; i < 4; i++)
16511 for (j = 0; j < NUM_BELT_PARTS; j++)
16513 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16514 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16516 graphic_info[graphic].anim_mode = anim_mode;
16520 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16522 InitRND(tape.random_seed);
16523 for (i = 0; i < num_random_calls; i++)
16527 if (game.num_random_calls != num_random_calls)
16529 Error(ERR_INFO, "number of random calls out of sync");
16530 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16531 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16532 Error(ERR_EXIT, "this should not happen -- please debug");
16536 void SaveEngineSnapshot()
16538 /* do not save snapshots from editor */
16539 if (level_editor_test_game)
16542 /* free previous snapshot buffers, if needed */
16543 FreeEngineSnapshotBuffers();
16545 /* copy some special values to a structure better suited for the snapshot */
16547 SaveEngineSnapshotValues_RND();
16548 SaveEngineSnapshotValues_EM();
16549 SaveEngineSnapshotValues_SP();
16551 /* save values stored in special snapshot structure */
16553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16557 /* save further RND engine values */
16559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16592 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16595 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16608 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16611 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16612 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16614 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16615 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16616 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16617 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16618 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16620 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16621 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16623 /* save level identification information */
16625 setString(&snapshot_level_identifier, leveldir_current->identifier);
16626 snapshot_level_nr = level_nr;
16629 ListNode *node = engine_snapshot_list_rnd;
16632 while (node != NULL)
16634 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16639 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16643 void LoadEngineSnapshot()
16645 /* restore generically stored snapshot buffers */
16647 LoadEngineSnapshotBuffers();
16649 /* restore special values from snapshot structure */
16651 LoadEngineSnapshotValues_RND();
16652 LoadEngineSnapshotValues_EM();
16653 LoadEngineSnapshotValues_SP();
16656 boolean CheckEngineSnapshot()
16658 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16659 snapshot_level_nr == level_nr);
16663 /* ---------- new game button stuff ---------------------------------------- */
16665 /* graphic position values for game buttons */
16666 #define GAME_BUTTON_XSIZE 30
16667 #define GAME_BUTTON_YSIZE 30
16668 #define GAME_BUTTON_XPOS 5
16669 #define GAME_BUTTON_YPOS 215
16670 #define SOUND_BUTTON_XPOS 5
16671 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16673 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16674 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16675 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16676 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16677 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16678 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16686 } gamebutton_info[NUM_GAME_BUTTONS] =
16690 &game.button.stop.x, &game.button.stop.y,
16691 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16696 &game.button.pause.x, &game.button.pause.y,
16697 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16698 GAME_CTRL_ID_PAUSE,
16702 &game.button.play.x, &game.button.play.y,
16703 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16708 &game.button.sound_music.x, &game.button.sound_music.y,
16709 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16710 SOUND_CTRL_ID_MUSIC,
16711 "background music on/off"
16714 &game.button.sound_loops.x, &game.button.sound_loops.y,
16715 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16716 SOUND_CTRL_ID_LOOPS,
16717 "sound loops on/off"
16720 &game.button.sound_simple.x,&game.button.sound_simple.y,
16721 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16722 SOUND_CTRL_ID_SIMPLE,
16723 "normal sounds on/off"
16727 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16732 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16733 GAME_CTRL_ID_PAUSE,
16737 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16742 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16743 SOUND_CTRL_ID_MUSIC,
16744 "background music on/off"
16747 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16748 SOUND_CTRL_ID_LOOPS,
16749 "sound loops on/off"
16752 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16753 SOUND_CTRL_ID_SIMPLE,
16754 "normal sounds on/off"
16759 void CreateGameButtons()
16763 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16765 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16766 struct GadgetInfo *gi;
16769 unsigned long event_mask;
16771 int gd_xoffset, gd_yoffset;
16772 int gd_x1, gd_x2, gd_y1, gd_y2;
16775 x = DX + *gamebutton_info[i].x;
16776 y = DY + *gamebutton_info[i].y;
16777 gd_xoffset = gamebutton_info[i].gd_x;
16778 gd_yoffset = gamebutton_info[i].gd_y;
16779 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16780 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16782 if (id == GAME_CTRL_ID_STOP ||
16783 id == GAME_CTRL_ID_PAUSE ||
16784 id == GAME_CTRL_ID_PLAY)
16786 button_type = GD_TYPE_NORMAL_BUTTON;
16788 event_mask = GD_EVENT_RELEASED;
16789 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16790 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16794 button_type = GD_TYPE_CHECK_BUTTON;
16796 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16797 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16798 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16799 event_mask = GD_EVENT_PRESSED;
16800 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16801 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16804 gi = CreateGadget(GDI_CUSTOM_ID, id,
16805 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16810 GDI_X, DX + gd_xoffset,
16811 GDI_Y, DY + gd_yoffset,
16813 GDI_WIDTH, GAME_BUTTON_XSIZE,
16814 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16815 GDI_TYPE, button_type,
16816 GDI_STATE, GD_BUTTON_UNPRESSED,
16817 GDI_CHECKED, checked,
16818 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16819 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16820 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16821 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16822 GDI_DIRECT_DRAW, FALSE,
16823 GDI_EVENT_MASK, event_mask,
16824 GDI_CALLBACK_ACTION, HandleGameButtons,
16828 Error(ERR_EXIT, "cannot create gadget");
16830 game_gadget[id] = gi;
16834 void FreeGameButtons()
16838 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16839 FreeGadget(game_gadget[i]);
16842 static void MapGameButtons()
16846 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16847 MapGadget(game_gadget[i]);
16850 void UnmapGameButtons()
16854 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16855 UnmapGadget(game_gadget[i]);
16858 void RedrawGameButtons()
16862 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16863 RedrawGadget(game_gadget[i]);
16866 static void HandleGameButtons(struct GadgetInfo *gi)
16868 int id = gi->custom_id;
16870 if (game_status != GAME_MODE_PLAYING)
16875 case GAME_CTRL_ID_STOP:
16879 RequestQuitGame(TRUE);
16882 case GAME_CTRL_ID_PAUSE:
16883 if (options.network)
16885 #if defined(NETWORK_AVALIABLE)
16887 SendToServer_ContinuePlaying();
16889 SendToServer_PausePlaying();
16893 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16896 case GAME_CTRL_ID_PLAY:
16899 #if defined(NETWORK_AVALIABLE)
16900 if (options.network)
16901 SendToServer_ContinuePlaying();
16905 tape.pausing = FALSE;
16906 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16911 case SOUND_CTRL_ID_MUSIC:
16912 if (setup.sound_music)
16914 setup.sound_music = FALSE;
16917 else if (audio.music_available)
16919 setup.sound = setup.sound_music = TRUE;
16921 SetAudioMode(setup.sound);
16927 case SOUND_CTRL_ID_LOOPS:
16928 if (setup.sound_loops)
16929 setup.sound_loops = FALSE;
16930 else if (audio.loops_available)
16932 setup.sound = setup.sound_loops = TRUE;
16933 SetAudioMode(setup.sound);
16937 case SOUND_CTRL_ID_SIMPLE:
16938 if (setup.sound_simple)
16939 setup.sound_simple = FALSE;
16940 else if (audio.sound_available)
16942 setup.sound = setup.sound_simple = TRUE;
16943 SetAudioMode(setup.sound);