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_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
70 #if USE_DELAYED_GFX_REDRAW
71 #define TEST_DrawLevelField(x, y) \
72 GfxRedraw[x][y] |= GFX_REDRAW_TILE
73 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
75 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
77 #define TEST_DrawTwinkleOnField(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
80 #define TEST_DrawLevelField(x, y) \
82 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
83 DrawLevelFieldCrumbledSand(x, y)
84 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
85 DrawLevelFieldCrumbledSandNeighbours(x, y)
86 #define TEST_DrawTwinkleOnField(x, y) \
87 DrawTwinkleOnField(x, y)
96 /* for MovePlayer() */
97 #define MP_NO_ACTION 0
100 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
102 /* for ScrollPlayer() */
103 #define SCROLL_INIT 0
104 #define SCROLL_GO_ON 1
106 /* for Bang()/Explode() */
107 #define EX_PHASE_START 0
108 #define EX_TYPE_NONE 0
109 #define EX_TYPE_NORMAL (1 << 0)
110 #define EX_TYPE_CENTER (1 << 1)
111 #define EX_TYPE_BORDER (1 << 2)
112 #define EX_TYPE_CROSS (1 << 3)
113 #define EX_TYPE_DYNA (1 << 4)
114 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
116 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
117 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
118 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
119 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
121 /* special positions in the game control window (relative to control window) */
122 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
123 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
124 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
125 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
126 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
127 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
128 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
129 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
130 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
131 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
132 #define XX_SCORE (PANEL_XPOS(game.panel.score))
133 #define YY_SCORE (PANEL_YPOS(game.panel.score))
134 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
135 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
136 #define XX_TIME (PANEL_XPOS(game.panel.time))
137 #define YY_TIME (PANEL_YPOS(game.panel.time))
139 /* special positions in the game control window (relative to main window) */
140 #define DX_LEVEL1 (DX + XX_LEVEL1)
141 #define DX_LEVEL2 (DX + XX_LEVEL2)
142 #define DX_LEVEL (DX + XX_LEVEL)
143 #define DY_LEVEL (DY + YY_LEVEL)
144 #define DX_EMERALDS (DX + XX_EMERALDS)
145 #define DY_EMERALDS (DY + YY_EMERALDS)
146 #define DX_DYNAMITE (DX + XX_DYNAMITE)
147 #define DY_DYNAMITE (DY + YY_DYNAMITE)
148 #define DX_KEYS (DX + XX_KEYS)
149 #define DY_KEYS (DY + YY_KEYS)
150 #define DX_SCORE (DX + XX_SCORE)
151 #define DY_SCORE (DY + YY_SCORE)
152 #define DX_TIME1 (DX + XX_TIME1)
153 #define DX_TIME2 (DX + XX_TIME2)
154 #define DX_TIME (DX + XX_TIME)
155 #define DY_TIME (DY + YY_TIME)
158 /* game panel display and control definitions */
160 #define GAME_PANEL_LEVEL_NUMBER 0
161 #define GAME_PANEL_GEMS 1
162 #define GAME_PANEL_INVENTORY_COUNT 2
163 #define GAME_PANEL_INVENTORY_FIRST_1 3
164 #define GAME_PANEL_INVENTORY_FIRST_2 4
165 #define GAME_PANEL_INVENTORY_FIRST_3 5
166 #define GAME_PANEL_INVENTORY_FIRST_4 6
167 #define GAME_PANEL_INVENTORY_FIRST_5 7
168 #define GAME_PANEL_INVENTORY_FIRST_6 8
169 #define GAME_PANEL_INVENTORY_FIRST_7 9
170 #define GAME_PANEL_INVENTORY_FIRST_8 10
171 #define GAME_PANEL_INVENTORY_LAST_1 11
172 #define GAME_PANEL_INVENTORY_LAST_2 12
173 #define GAME_PANEL_INVENTORY_LAST_3 13
174 #define GAME_PANEL_INVENTORY_LAST_4 14
175 #define GAME_PANEL_INVENTORY_LAST_5 15
176 #define GAME_PANEL_INVENTORY_LAST_6 16
177 #define GAME_PANEL_INVENTORY_LAST_7 17
178 #define GAME_PANEL_INVENTORY_LAST_8 18
179 #define GAME_PANEL_KEY_1 19
180 #define GAME_PANEL_KEY_2 20
181 #define GAME_PANEL_KEY_3 21
182 #define GAME_PANEL_KEY_4 22
183 #define GAME_PANEL_KEY_5 23
184 #define GAME_PANEL_KEY_6 24
185 #define GAME_PANEL_KEY_7 25
186 #define GAME_PANEL_KEY_8 26
187 #define GAME_PANEL_KEY_WHITE 27
188 #define GAME_PANEL_KEY_WHITE_COUNT 28
189 #define GAME_PANEL_SCORE 29
190 #define GAME_PANEL_HIGHSCORE 30
191 #define GAME_PANEL_TIME 31
192 #define GAME_PANEL_TIME_HH 32
193 #define GAME_PANEL_TIME_MM 33
194 #define GAME_PANEL_TIME_SS 34
195 #define GAME_PANEL_SHIELD_NORMAL 35
196 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
197 #define GAME_PANEL_SHIELD_DEADLY 37
198 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
199 #define GAME_PANEL_EXIT 39
200 #define GAME_PANEL_EMC_MAGIC_BALL 40
201 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
202 #define GAME_PANEL_LIGHT_SWITCH 42
203 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
204 #define GAME_PANEL_TIMEGATE_SWITCH 44
205 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
206 #define GAME_PANEL_SWITCHGATE_SWITCH 46
207 #define GAME_PANEL_EMC_LENSES 47
208 #define GAME_PANEL_EMC_LENSES_TIME 48
209 #define GAME_PANEL_EMC_MAGNIFIER 49
210 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
211 #define GAME_PANEL_BALLOON_SWITCH 51
212 #define GAME_PANEL_DYNABOMB_NUMBER 52
213 #define GAME_PANEL_DYNABOMB_SIZE 53
214 #define GAME_PANEL_DYNABOMB_POWER 54
215 #define GAME_PANEL_PENGUINS 55
216 #define GAME_PANEL_SOKOBAN_OBJECTS 56
217 #define GAME_PANEL_SOKOBAN_FIELDS 57
218 #define GAME_PANEL_ROBOT_WHEEL 58
219 #define GAME_PANEL_CONVEYOR_BELT_1 59
220 #define GAME_PANEL_CONVEYOR_BELT_2 60
221 #define GAME_PANEL_CONVEYOR_BELT_3 61
222 #define GAME_PANEL_CONVEYOR_BELT_4 62
223 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
224 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
225 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
226 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
227 #define GAME_PANEL_MAGIC_WALL 67
228 #define GAME_PANEL_MAGIC_WALL_TIME 68
229 #define GAME_PANEL_GRAVITY_STATE 69
230 #define GAME_PANEL_GRAPHIC_1 70
231 #define GAME_PANEL_GRAPHIC_2 71
232 #define GAME_PANEL_GRAPHIC_3 72
233 #define GAME_PANEL_GRAPHIC_4 73
234 #define GAME_PANEL_GRAPHIC_5 74
235 #define GAME_PANEL_GRAPHIC_6 75
236 #define GAME_PANEL_GRAPHIC_7 76
237 #define GAME_PANEL_GRAPHIC_8 77
238 #define GAME_PANEL_ELEMENT_1 78
239 #define GAME_PANEL_ELEMENT_2 79
240 #define GAME_PANEL_ELEMENT_3 80
241 #define GAME_PANEL_ELEMENT_4 81
242 #define GAME_PANEL_ELEMENT_5 82
243 #define GAME_PANEL_ELEMENT_6 83
244 #define GAME_PANEL_ELEMENT_7 84
245 #define GAME_PANEL_ELEMENT_8 85
246 #define GAME_PANEL_ELEMENT_COUNT_1 86
247 #define GAME_PANEL_ELEMENT_COUNT_2 87
248 #define GAME_PANEL_ELEMENT_COUNT_3 88
249 #define GAME_PANEL_ELEMENT_COUNT_4 89
250 #define GAME_PANEL_ELEMENT_COUNT_5 90
251 #define GAME_PANEL_ELEMENT_COUNT_6 91
252 #define GAME_PANEL_ELEMENT_COUNT_7 92
253 #define GAME_PANEL_ELEMENT_COUNT_8 93
254 #define GAME_PANEL_CE_SCORE_1 94
255 #define GAME_PANEL_CE_SCORE_2 95
256 #define GAME_PANEL_CE_SCORE_3 96
257 #define GAME_PANEL_CE_SCORE_4 97
258 #define GAME_PANEL_CE_SCORE_5 98
259 #define GAME_PANEL_CE_SCORE_6 99
260 #define GAME_PANEL_CE_SCORE_7 100
261 #define GAME_PANEL_CE_SCORE_8 101
262 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
263 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
264 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
265 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
266 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
267 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
268 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
269 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
270 #define GAME_PANEL_PLAYER_NAME 110
271 #define GAME_PANEL_LEVEL_NAME 111
272 #define GAME_PANEL_LEVEL_AUTHOR 112
274 #define NUM_GAME_PANEL_CONTROLS 113
276 struct GamePanelOrderInfo
282 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
284 struct GamePanelControlInfo
288 struct TextPosInfo *pos;
291 int value, last_value;
292 int frame, last_frame;
297 static struct GamePanelControlInfo game_panel_controls[] =
300 GAME_PANEL_LEVEL_NUMBER,
301 &game.panel.level_number,
310 GAME_PANEL_INVENTORY_COUNT,
311 &game.panel.inventory_count,
315 GAME_PANEL_INVENTORY_FIRST_1,
316 &game.panel.inventory_first[0],
320 GAME_PANEL_INVENTORY_FIRST_2,
321 &game.panel.inventory_first[1],
325 GAME_PANEL_INVENTORY_FIRST_3,
326 &game.panel.inventory_first[2],
330 GAME_PANEL_INVENTORY_FIRST_4,
331 &game.panel.inventory_first[3],
335 GAME_PANEL_INVENTORY_FIRST_5,
336 &game.panel.inventory_first[4],
340 GAME_PANEL_INVENTORY_FIRST_6,
341 &game.panel.inventory_first[5],
345 GAME_PANEL_INVENTORY_FIRST_7,
346 &game.panel.inventory_first[6],
350 GAME_PANEL_INVENTORY_FIRST_8,
351 &game.panel.inventory_first[7],
355 GAME_PANEL_INVENTORY_LAST_1,
356 &game.panel.inventory_last[0],
360 GAME_PANEL_INVENTORY_LAST_2,
361 &game.panel.inventory_last[1],
365 GAME_PANEL_INVENTORY_LAST_3,
366 &game.panel.inventory_last[2],
370 GAME_PANEL_INVENTORY_LAST_4,
371 &game.panel.inventory_last[3],
375 GAME_PANEL_INVENTORY_LAST_5,
376 &game.panel.inventory_last[4],
380 GAME_PANEL_INVENTORY_LAST_6,
381 &game.panel.inventory_last[5],
385 GAME_PANEL_INVENTORY_LAST_7,
386 &game.panel.inventory_last[6],
390 GAME_PANEL_INVENTORY_LAST_8,
391 &game.panel.inventory_last[7],
435 GAME_PANEL_KEY_WHITE,
436 &game.panel.key_white,
440 GAME_PANEL_KEY_WHITE_COUNT,
441 &game.panel.key_white_count,
450 GAME_PANEL_HIGHSCORE,
451 &game.panel.highscore,
475 GAME_PANEL_SHIELD_NORMAL,
476 &game.panel.shield_normal,
480 GAME_PANEL_SHIELD_NORMAL_TIME,
481 &game.panel.shield_normal_time,
485 GAME_PANEL_SHIELD_DEADLY,
486 &game.panel.shield_deadly,
490 GAME_PANEL_SHIELD_DEADLY_TIME,
491 &game.panel.shield_deadly_time,
500 GAME_PANEL_EMC_MAGIC_BALL,
501 &game.panel.emc_magic_ball,
505 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
506 &game.panel.emc_magic_ball_switch,
510 GAME_PANEL_LIGHT_SWITCH,
511 &game.panel.light_switch,
515 GAME_PANEL_LIGHT_SWITCH_TIME,
516 &game.panel.light_switch_time,
520 GAME_PANEL_TIMEGATE_SWITCH,
521 &game.panel.timegate_switch,
525 GAME_PANEL_TIMEGATE_SWITCH_TIME,
526 &game.panel.timegate_switch_time,
530 GAME_PANEL_SWITCHGATE_SWITCH,
531 &game.panel.switchgate_switch,
535 GAME_PANEL_EMC_LENSES,
536 &game.panel.emc_lenses,
540 GAME_PANEL_EMC_LENSES_TIME,
541 &game.panel.emc_lenses_time,
545 GAME_PANEL_EMC_MAGNIFIER,
546 &game.panel.emc_magnifier,
550 GAME_PANEL_EMC_MAGNIFIER_TIME,
551 &game.panel.emc_magnifier_time,
555 GAME_PANEL_BALLOON_SWITCH,
556 &game.panel.balloon_switch,
560 GAME_PANEL_DYNABOMB_NUMBER,
561 &game.panel.dynabomb_number,
565 GAME_PANEL_DYNABOMB_SIZE,
566 &game.panel.dynabomb_size,
570 GAME_PANEL_DYNABOMB_POWER,
571 &game.panel.dynabomb_power,
576 &game.panel.penguins,
580 GAME_PANEL_SOKOBAN_OBJECTS,
581 &game.panel.sokoban_objects,
585 GAME_PANEL_SOKOBAN_FIELDS,
586 &game.panel.sokoban_fields,
590 GAME_PANEL_ROBOT_WHEEL,
591 &game.panel.robot_wheel,
595 GAME_PANEL_CONVEYOR_BELT_1,
596 &game.panel.conveyor_belt[0],
600 GAME_PANEL_CONVEYOR_BELT_2,
601 &game.panel.conveyor_belt[1],
605 GAME_PANEL_CONVEYOR_BELT_3,
606 &game.panel.conveyor_belt[2],
610 GAME_PANEL_CONVEYOR_BELT_4,
611 &game.panel.conveyor_belt[3],
615 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
616 &game.panel.conveyor_belt_switch[0],
620 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
621 &game.panel.conveyor_belt_switch[1],
625 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
626 &game.panel.conveyor_belt_switch[2],
630 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
631 &game.panel.conveyor_belt_switch[3],
635 GAME_PANEL_MAGIC_WALL,
636 &game.panel.magic_wall,
640 GAME_PANEL_MAGIC_WALL_TIME,
641 &game.panel.magic_wall_time,
645 GAME_PANEL_GRAVITY_STATE,
646 &game.panel.gravity_state,
650 GAME_PANEL_GRAPHIC_1,
651 &game.panel.graphic[0],
655 GAME_PANEL_GRAPHIC_2,
656 &game.panel.graphic[1],
660 GAME_PANEL_GRAPHIC_3,
661 &game.panel.graphic[2],
665 GAME_PANEL_GRAPHIC_4,
666 &game.panel.graphic[3],
670 GAME_PANEL_GRAPHIC_5,
671 &game.panel.graphic[4],
675 GAME_PANEL_GRAPHIC_6,
676 &game.panel.graphic[5],
680 GAME_PANEL_GRAPHIC_7,
681 &game.panel.graphic[6],
685 GAME_PANEL_GRAPHIC_8,
686 &game.panel.graphic[7],
690 GAME_PANEL_ELEMENT_1,
691 &game.panel.element[0],
695 GAME_PANEL_ELEMENT_2,
696 &game.panel.element[1],
700 GAME_PANEL_ELEMENT_3,
701 &game.panel.element[2],
705 GAME_PANEL_ELEMENT_4,
706 &game.panel.element[3],
710 GAME_PANEL_ELEMENT_5,
711 &game.panel.element[4],
715 GAME_PANEL_ELEMENT_6,
716 &game.panel.element[5],
720 GAME_PANEL_ELEMENT_7,
721 &game.panel.element[6],
725 GAME_PANEL_ELEMENT_8,
726 &game.panel.element[7],
730 GAME_PANEL_ELEMENT_COUNT_1,
731 &game.panel.element_count[0],
735 GAME_PANEL_ELEMENT_COUNT_2,
736 &game.panel.element_count[1],
740 GAME_PANEL_ELEMENT_COUNT_3,
741 &game.panel.element_count[2],
745 GAME_PANEL_ELEMENT_COUNT_4,
746 &game.panel.element_count[3],
750 GAME_PANEL_ELEMENT_COUNT_5,
751 &game.panel.element_count[4],
755 GAME_PANEL_ELEMENT_COUNT_6,
756 &game.panel.element_count[5],
760 GAME_PANEL_ELEMENT_COUNT_7,
761 &game.panel.element_count[6],
765 GAME_PANEL_ELEMENT_COUNT_8,
766 &game.panel.element_count[7],
770 GAME_PANEL_CE_SCORE_1,
771 &game.panel.ce_score[0],
775 GAME_PANEL_CE_SCORE_2,
776 &game.panel.ce_score[1],
780 GAME_PANEL_CE_SCORE_3,
781 &game.panel.ce_score[2],
785 GAME_PANEL_CE_SCORE_4,
786 &game.panel.ce_score[3],
790 GAME_PANEL_CE_SCORE_5,
791 &game.panel.ce_score[4],
795 GAME_PANEL_CE_SCORE_6,
796 &game.panel.ce_score[5],
800 GAME_PANEL_CE_SCORE_7,
801 &game.panel.ce_score[6],
805 GAME_PANEL_CE_SCORE_8,
806 &game.panel.ce_score[7],
810 GAME_PANEL_CE_SCORE_1_ELEMENT,
811 &game.panel.ce_score_element[0],
815 GAME_PANEL_CE_SCORE_2_ELEMENT,
816 &game.panel.ce_score_element[1],
820 GAME_PANEL_CE_SCORE_3_ELEMENT,
821 &game.panel.ce_score_element[2],
825 GAME_PANEL_CE_SCORE_4_ELEMENT,
826 &game.panel.ce_score_element[3],
830 GAME_PANEL_CE_SCORE_5_ELEMENT,
831 &game.panel.ce_score_element[4],
835 GAME_PANEL_CE_SCORE_6_ELEMENT,
836 &game.panel.ce_score_element[5],
840 GAME_PANEL_CE_SCORE_7_ELEMENT,
841 &game.panel.ce_score_element[6],
845 GAME_PANEL_CE_SCORE_8_ELEMENT,
846 &game.panel.ce_score_element[7],
850 GAME_PANEL_PLAYER_NAME,
851 &game.panel.player_name,
855 GAME_PANEL_LEVEL_NAME,
856 &game.panel.level_name,
860 GAME_PANEL_LEVEL_AUTHOR,
861 &game.panel.level_author,
874 /* values for delayed check of falling and moving elements and for collision */
875 #define CHECK_DELAY_MOVING 3
876 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
877 #define CHECK_DELAY_COLLISION 2
878 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
880 /* values for initial player move delay (initial delay counter value) */
881 #define INITIAL_MOVE_DELAY_OFF -1
882 #define INITIAL_MOVE_DELAY_ON 0
884 /* values for player movement speed (which is in fact a delay value) */
885 #define MOVE_DELAY_MIN_SPEED 32
886 #define MOVE_DELAY_NORMAL_SPEED 8
887 #define MOVE_DELAY_HIGH_SPEED 4
888 #define MOVE_DELAY_MAX_SPEED 1
890 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
891 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
893 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
894 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
896 /* values for other actions */
897 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
898 #define MOVE_STEPSIZE_MIN (1)
899 #define MOVE_STEPSIZE_MAX (TILEX)
901 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
902 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
904 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
906 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
907 RND(element_info[e].push_delay_random))
908 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
909 RND(element_info[e].drop_delay_random))
910 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
911 RND(element_info[e].move_delay_random))
912 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
913 (element_info[e].move_delay_random))
914 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
915 RND(element_info[e].ce_value_random_initial))
916 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
917 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
918 RND((c)->delay_random * (c)->delay_frames))
919 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
920 RND((c)->delay_random))
923 #define GET_VALID_RUNTIME_ELEMENT(e) \
924 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
926 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
927 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
928 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
929 (be) + (e) - EL_SELF)
931 #define GET_PLAYER_FROM_BITS(p) \
932 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
934 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
935 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
936 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
937 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
938 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
939 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
940 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
941 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
942 RESOLVED_REFERENCE_ELEMENT(be, e) : \
945 #define CAN_GROW_INTO(e) \
946 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
949 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
952 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
953 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
954 (CAN_MOVE_INTO_ACID(e) && \
955 Feld[x][y] == EL_ACID) || \
958 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
959 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
960 (CAN_MOVE_INTO_ACID(e) && \
961 Feld[x][y] == EL_ACID) || \
964 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
965 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
967 (CAN_MOVE_INTO_ACID(e) && \
968 Feld[x][y] == EL_ACID) || \
969 (DONT_COLLIDE_WITH(e) && \
971 !PLAYER_ENEMY_PROTECTED(x, y))))
973 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
974 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
976 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
977 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
979 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
980 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
982 #define ANDROID_CAN_CLONE_FIELD(x, y) \
983 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
984 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
986 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
987 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
989 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
990 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
992 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
993 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
995 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
996 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
998 #define PIG_CAN_ENTER_FIELD(e, x, y) \
999 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1001 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1002 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1003 Feld[x][y] == EL_EM_EXIT_OPEN || \
1004 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1005 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1006 IS_FOOD_PENGUIN(Feld[x][y])))
1007 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1008 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1010 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1011 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1013 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1014 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1016 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1017 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1018 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1020 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1022 #define CE_ENTER_FIELD_COND(e, x, y) \
1023 (!IS_PLAYER(x, y) && \
1024 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1026 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1027 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1029 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1030 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1032 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1033 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1034 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1035 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1037 /* game button identifiers */
1038 #define GAME_CTRL_ID_STOP 0
1039 #define GAME_CTRL_ID_PAUSE 1
1040 #define GAME_CTRL_ID_PLAY 2
1041 #define SOUND_CTRL_ID_MUSIC 3
1042 #define SOUND_CTRL_ID_LOOPS 4
1043 #define SOUND_CTRL_ID_SIMPLE 5
1045 #define NUM_GAME_BUTTONS 6
1048 /* forward declaration for internal use */
1050 static void CreateField(int, int, int);
1052 static void ResetGfxAnimation(int, int);
1054 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1055 static void AdvanceFrameAndPlayerCounters(int);
1057 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1058 static boolean MovePlayer(struct PlayerInfo *, int, int);
1059 static void ScrollPlayer(struct PlayerInfo *, int);
1060 static void ScrollScreen(struct PlayerInfo *, int);
1062 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1063 static boolean DigFieldByCE(int, int, int);
1064 static boolean SnapField(struct PlayerInfo *, int, int);
1065 static boolean DropElement(struct PlayerInfo *);
1067 static void InitBeltMovement(void);
1068 static void CloseAllOpenTimegates(void);
1069 static void CheckGravityMovement(struct PlayerInfo *);
1070 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1071 static void KillPlayerUnlessEnemyProtected(int, int);
1072 static void KillPlayerUnlessExplosionProtected(int, int);
1074 static void TestIfPlayerTouchesCustomElement(int, int);
1075 static void TestIfElementTouchesCustomElement(int, int);
1076 static void TestIfElementHitsCustomElement(int, int, int);
1078 static void TestIfElementSmashesCustomElement(int, int, int);
1081 static void HandleElementChange(int, int, int);
1082 static void ExecuteCustomElementAction(int, int, int, int);
1083 static boolean ChangeElement(int, int, int, int);
1085 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1086 #define CheckTriggeredElementChange(x, y, e, ev) \
1087 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1088 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1089 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1090 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1091 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1092 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1093 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1095 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1096 #define CheckElementChange(x, y, e, te, ev) \
1097 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1098 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1099 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1100 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1101 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1103 static void PlayLevelSound(int, int, int);
1104 static void PlayLevelSoundNearest(int, int, int);
1105 static void PlayLevelSoundAction(int, int, int);
1106 static void PlayLevelSoundElementAction(int, int, int, int);
1107 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1108 static void PlayLevelSoundActionIfLoop(int, int, int);
1109 static void StopLevelSoundActionIfLoop(int, int, int);
1110 static void PlayLevelMusic();
1112 static void MapGameButtons();
1113 static void HandleGameButtons(struct GadgetInfo *);
1115 int AmoebeNachbarNr(int, int);
1116 void AmoebeUmwandeln(int, int);
1117 void ContinueMoving(int, int);
1118 void Bang(int, int);
1119 void InitMovDir(int, int);
1120 void InitAmoebaNr(int, int);
1121 int NewHiScore(void);
1123 void TestIfGoodThingHitsBadThing(int, int, int);
1124 void TestIfBadThingHitsGoodThing(int, int, int);
1125 void TestIfPlayerTouchesBadThing(int, int);
1126 void TestIfPlayerRunsIntoBadThing(int, int, int);
1127 void TestIfBadThingTouchesPlayer(int, int);
1128 void TestIfBadThingRunsIntoPlayer(int, int, int);
1129 void TestIfFriendTouchesBadThing(int, int);
1130 void TestIfBadThingTouchesFriend(int, int);
1131 void TestIfBadThingTouchesOtherBadThing(int, int);
1132 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1134 void KillPlayer(struct PlayerInfo *);
1135 void BuryPlayer(struct PlayerInfo *);
1136 void RemovePlayer(struct PlayerInfo *);
1138 static int getInvisibleActiveFromInvisibleElement(int);
1139 static int getInvisibleFromInvisibleActiveElement(int);
1141 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1143 /* for detection of endless loops, caused by custom element programming */
1144 /* (using maximal playfield width x 10 is just a rough approximation) */
1145 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1147 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1149 if (recursion_loop_detected) \
1152 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1154 recursion_loop_detected = TRUE; \
1155 recursion_loop_element = (e); \
1158 recursion_loop_depth++; \
1161 #define RECURSION_LOOP_DETECTION_END() \
1163 recursion_loop_depth--; \
1166 static int recursion_loop_depth;
1167 static boolean recursion_loop_detected;
1168 static boolean recursion_loop_element;
1171 /* ------------------------------------------------------------------------- */
1172 /* definition of elements that automatically change to other elements after */
1173 /* a specified time, eventually calling a function when changing */
1174 /* ------------------------------------------------------------------------- */
1176 /* forward declaration for changer functions */
1177 static void InitBuggyBase(int, int);
1178 static void WarnBuggyBase(int, int);
1180 static void InitTrap(int, int);
1181 static void ActivateTrap(int, int);
1182 static void ChangeActiveTrap(int, int);
1184 static void InitRobotWheel(int, int);
1185 static void RunRobotWheel(int, int);
1186 static void StopRobotWheel(int, int);
1188 static void InitTimegateWheel(int, int);
1189 static void RunTimegateWheel(int, int);
1191 static void InitMagicBallDelay(int, int);
1192 static void ActivateMagicBall(int, int);
1194 struct ChangingElementInfo
1199 void (*pre_change_function)(int x, int y);
1200 void (*change_function)(int x, int y);
1201 void (*post_change_function)(int x, int y);
1204 static struct ChangingElementInfo change_delay_list[] =
1239 EL_STEEL_EXIT_OPENING,
1247 EL_STEEL_EXIT_CLOSING,
1248 EL_STEEL_EXIT_CLOSED,
1275 EL_EM_STEEL_EXIT_OPENING,
1276 EL_EM_STEEL_EXIT_OPEN,
1283 EL_EM_STEEL_EXIT_CLOSING,
1287 EL_EM_STEEL_EXIT_CLOSED,
1311 EL_SWITCHGATE_OPENING,
1319 EL_SWITCHGATE_CLOSING,
1320 EL_SWITCHGATE_CLOSED,
1327 EL_TIMEGATE_OPENING,
1335 EL_TIMEGATE_CLOSING,
1344 EL_ACID_SPLASH_LEFT,
1352 EL_ACID_SPLASH_RIGHT,
1361 EL_SP_BUGGY_BASE_ACTIVATING,
1368 EL_SP_BUGGY_BASE_ACTIVATING,
1369 EL_SP_BUGGY_BASE_ACTIVE,
1376 EL_SP_BUGGY_BASE_ACTIVE,
1400 EL_ROBOT_WHEEL_ACTIVE,
1408 EL_TIMEGATE_SWITCH_ACTIVE,
1416 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1417 EL_DC_TIMEGATE_SWITCH,
1424 EL_EMC_MAGIC_BALL_ACTIVE,
1425 EL_EMC_MAGIC_BALL_ACTIVE,
1432 EL_EMC_SPRING_BUMPER_ACTIVE,
1433 EL_EMC_SPRING_BUMPER,
1440 EL_DIAGONAL_SHRINKING,
1448 EL_DIAGONAL_GROWING,
1469 int push_delay_fixed, push_delay_random;
1473 { EL_SPRING, 0, 0 },
1474 { EL_BALLOON, 0, 0 },
1476 { EL_SOKOBAN_OBJECT, 2, 0 },
1477 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1478 { EL_SATELLITE, 2, 0 },
1479 { EL_SP_DISK_YELLOW, 2, 0 },
1481 { EL_UNDEFINED, 0, 0 },
1489 move_stepsize_list[] =
1491 { EL_AMOEBA_DROP, 2 },
1492 { EL_AMOEBA_DROPPING, 2 },
1493 { EL_QUICKSAND_FILLING, 1 },
1494 { EL_QUICKSAND_EMPTYING, 1 },
1495 { EL_QUICKSAND_FAST_FILLING, 2 },
1496 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1497 { EL_MAGIC_WALL_FILLING, 2 },
1498 { EL_MAGIC_WALL_EMPTYING, 2 },
1499 { EL_BD_MAGIC_WALL_FILLING, 2 },
1500 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1501 { EL_DC_MAGIC_WALL_FILLING, 2 },
1502 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1504 { EL_UNDEFINED, 0 },
1512 collect_count_list[] =
1515 { EL_BD_DIAMOND, 1 },
1516 { EL_EMERALD_YELLOW, 1 },
1517 { EL_EMERALD_RED, 1 },
1518 { EL_EMERALD_PURPLE, 1 },
1520 { EL_SP_INFOTRON, 1 },
1524 { EL_UNDEFINED, 0 },
1532 access_direction_list[] =
1534 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1535 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1536 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1537 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1538 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1539 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1540 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1541 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1542 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1543 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1544 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1546 { EL_SP_PORT_LEFT, MV_RIGHT },
1547 { EL_SP_PORT_RIGHT, MV_LEFT },
1548 { EL_SP_PORT_UP, MV_DOWN },
1549 { EL_SP_PORT_DOWN, MV_UP },
1550 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1551 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1552 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1553 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1554 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1555 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1556 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1557 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1561 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1562 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1563 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1564 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1566 { EL_UNDEFINED, MV_NONE }
1569 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1571 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1572 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1573 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1574 IS_JUST_CHANGING(x, y))
1576 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1578 /* static variables for playfield scan mode (scanning forward or backward) */
1579 static int playfield_scan_start_x = 0;
1580 static int playfield_scan_start_y = 0;
1581 static int playfield_scan_delta_x = 1;
1582 static int playfield_scan_delta_y = 1;
1584 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1585 (y) >= 0 && (y) <= lev_fieldy - 1; \
1586 (y) += playfield_scan_delta_y) \
1587 for ((x) = playfield_scan_start_x; \
1588 (x) >= 0 && (x) <= lev_fieldx - 1; \
1589 (x) += playfield_scan_delta_x)
1592 void DEBUG_SetMaximumDynamite()
1596 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1597 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1598 local_player->inventory_element[local_player->inventory_size++] =
1603 static void InitPlayfieldScanModeVars()
1605 if (game.use_reverse_scan_direction)
1607 playfield_scan_start_x = lev_fieldx - 1;
1608 playfield_scan_start_y = lev_fieldy - 1;
1610 playfield_scan_delta_x = -1;
1611 playfield_scan_delta_y = -1;
1615 playfield_scan_start_x = 0;
1616 playfield_scan_start_y = 0;
1618 playfield_scan_delta_x = 1;
1619 playfield_scan_delta_y = 1;
1623 static void InitPlayfieldScanMode(int mode)
1625 game.use_reverse_scan_direction =
1626 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1628 InitPlayfieldScanModeVars();
1631 static int get_move_delay_from_stepsize(int move_stepsize)
1634 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1636 /* make sure that stepsize value is always a power of 2 */
1637 move_stepsize = (1 << log_2(move_stepsize));
1639 return TILEX / move_stepsize;
1642 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1645 int player_nr = player->index_nr;
1646 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1647 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1649 /* do no immediately change move delay -- the player might just be moving */
1650 player->move_delay_value_next = move_delay;
1652 /* information if player can move must be set separately */
1653 player->cannot_move = cannot_move;
1657 player->move_delay = game.initial_move_delay[player_nr];
1658 player->move_delay_value = game.initial_move_delay_value[player_nr];
1660 player->move_delay_value_next = -1;
1662 player->move_delay_reset_counter = 0;
1666 void GetPlayerConfig()
1668 GameFrameDelay = setup.game_frame_delay;
1670 if (!audio.sound_available)
1671 setup.sound_simple = FALSE;
1673 if (!audio.loops_available)
1674 setup.sound_loops = FALSE;
1676 if (!audio.music_available)
1677 setup.sound_music = FALSE;
1679 if (!video.fullscreen_available)
1680 setup.fullscreen = FALSE;
1682 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1684 SetAudioMode(setup.sound);
1688 int GetElementFromGroupElement(int element)
1690 if (IS_GROUP_ELEMENT(element))
1692 struct ElementGroupInfo *group = element_info[element].group;
1693 int last_anim_random_frame = gfx.anim_random_frame;
1696 if (group->choice_mode == ANIM_RANDOM)
1697 gfx.anim_random_frame = RND(group->num_elements_resolved);
1699 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1700 group->choice_mode, 0,
1703 if (group->choice_mode == ANIM_RANDOM)
1704 gfx.anim_random_frame = last_anim_random_frame;
1706 group->choice_pos++;
1708 element = group->element_resolved[element_pos];
1714 static void InitPlayerField(int x, int y, int element, boolean init_game)
1716 if (element == EL_SP_MURPHY)
1720 if (stored_player[0].present)
1722 Feld[x][y] = EL_SP_MURPHY_CLONE;
1728 stored_player[0].initial_element = element;
1729 stored_player[0].use_murphy = TRUE;
1731 if (!level.use_artwork_element[0])
1732 stored_player[0].artwork_element = EL_SP_MURPHY;
1735 Feld[x][y] = EL_PLAYER_1;
1741 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1742 int jx = player->jx, jy = player->jy;
1744 player->present = TRUE;
1746 player->block_last_field = (element == EL_SP_MURPHY ?
1747 level.sp_block_last_field :
1748 level.block_last_field);
1750 /* ---------- initialize player's last field block delay --------------- */
1752 /* always start with reliable default value (no adjustment needed) */
1753 player->block_delay_adjustment = 0;
1755 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1756 if (player->block_last_field && element == EL_SP_MURPHY)
1757 player->block_delay_adjustment = 1;
1759 /* special case 2: in game engines before 3.1.1, blocking was different */
1760 if (game.use_block_last_field_bug)
1761 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1763 if (!options.network || player->connected)
1765 player->active = TRUE;
1767 /* remove potentially duplicate players */
1768 if (StorePlayer[jx][jy] == Feld[x][y])
1769 StorePlayer[jx][jy] = 0;
1771 StorePlayer[x][y] = Feld[x][y];
1775 printf("Player %d activated.\n", player->element_nr);
1776 printf("[Local player is %d and currently %s.]\n",
1777 local_player->element_nr,
1778 local_player->active ? "active" : "not active");
1782 Feld[x][y] = EL_EMPTY;
1784 player->jx = player->last_jx = x;
1785 player->jy = player->last_jy = y;
1788 #if USE_PLAYER_REANIMATION
1791 int player_nr = GET_PLAYER_NR(element);
1792 struct PlayerInfo *player = &stored_player[player_nr];
1795 player->killed = FALSE; /* if player was just killed, reanimate him */
1800 static void InitField(int x, int y, boolean init_game)
1802 int element = Feld[x][y];
1811 InitPlayerField(x, y, element, init_game);
1814 case EL_SOKOBAN_FIELD_PLAYER:
1815 element = Feld[x][y] = EL_PLAYER_1;
1816 InitField(x, y, init_game);
1818 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1819 InitField(x, y, init_game);
1822 case EL_SOKOBAN_FIELD_EMPTY:
1823 local_player->sokobanfields_still_needed++;
1827 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1828 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1829 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1830 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1831 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1832 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1833 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1834 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1845 case EL_SPACESHIP_RIGHT:
1846 case EL_SPACESHIP_UP:
1847 case EL_SPACESHIP_LEFT:
1848 case EL_SPACESHIP_DOWN:
1849 case EL_BD_BUTTERFLY:
1850 case EL_BD_BUTTERFLY_RIGHT:
1851 case EL_BD_BUTTERFLY_UP:
1852 case EL_BD_BUTTERFLY_LEFT:
1853 case EL_BD_BUTTERFLY_DOWN:
1855 case EL_BD_FIREFLY_RIGHT:
1856 case EL_BD_FIREFLY_UP:
1857 case EL_BD_FIREFLY_LEFT:
1858 case EL_BD_FIREFLY_DOWN:
1859 case EL_PACMAN_RIGHT:
1861 case EL_PACMAN_LEFT:
1862 case EL_PACMAN_DOWN:
1864 case EL_YAMYAM_LEFT:
1865 case EL_YAMYAM_RIGHT:
1867 case EL_YAMYAM_DOWN:
1868 case EL_DARK_YAMYAM:
1871 case EL_SP_SNIKSNAK:
1872 case EL_SP_ELECTRON:
1881 case EL_AMOEBA_FULL:
1886 case EL_AMOEBA_DROP:
1887 if (y == lev_fieldy - 1)
1889 Feld[x][y] = EL_AMOEBA_GROWING;
1890 Store[x][y] = EL_AMOEBA_WET;
1894 case EL_DYNAMITE_ACTIVE:
1895 case EL_SP_DISK_RED_ACTIVE:
1896 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1897 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1898 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1899 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1900 MovDelay[x][y] = 96;
1903 case EL_EM_DYNAMITE_ACTIVE:
1904 MovDelay[x][y] = 32;
1908 local_player->lights_still_needed++;
1912 local_player->friends_still_needed++;
1917 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1920 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1921 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1922 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1923 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1924 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1925 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1926 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1927 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1928 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1929 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1930 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1931 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1934 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1935 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1936 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1938 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1940 game.belt_dir[belt_nr] = belt_dir;
1941 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1943 else /* more than one switch -- set it like the first switch */
1945 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1950 #if !USE_BOTH_SWITCHGATE_SWITCHES
1951 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1953 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1956 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1958 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1962 case EL_LIGHT_SWITCH_ACTIVE:
1964 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1967 case EL_INVISIBLE_STEELWALL:
1968 case EL_INVISIBLE_WALL:
1969 case EL_INVISIBLE_SAND:
1970 if (game.light_time_left > 0 ||
1971 game.lenses_time_left > 0)
1972 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1975 case EL_EMC_MAGIC_BALL:
1976 if (game.ball_state)
1977 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1980 case EL_EMC_MAGIC_BALL_SWITCH:
1981 if (game.ball_state)
1982 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1985 case EL_TRIGGER_PLAYER:
1986 case EL_TRIGGER_ELEMENT:
1987 case EL_TRIGGER_CE_VALUE:
1988 case EL_TRIGGER_CE_SCORE:
1990 case EL_ANY_ELEMENT:
1991 case EL_CURRENT_CE_VALUE:
1992 case EL_CURRENT_CE_SCORE:
2009 /* reference elements should not be used on the playfield */
2010 Feld[x][y] = EL_EMPTY;
2014 if (IS_CUSTOM_ELEMENT(element))
2016 if (CAN_MOVE(element))
2019 #if USE_NEW_CUSTOM_VALUE
2020 if (!element_info[element].use_last_ce_value || init_game)
2021 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2024 else if (IS_GROUP_ELEMENT(element))
2026 Feld[x][y] = GetElementFromGroupElement(element);
2028 InitField(x, y, init_game);
2035 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2038 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2040 InitField(x, y, init_game);
2042 /* not needed to call InitMovDir() -- already done by InitField()! */
2043 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2044 CAN_MOVE(Feld[x][y]))
2048 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2050 int old_element = Feld[x][y];
2052 InitField(x, y, init_game);
2054 /* not needed to call InitMovDir() -- already done by InitField()! */
2055 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2056 CAN_MOVE(old_element) &&
2057 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2060 /* this case is in fact a combination of not less than three bugs:
2061 first, it calls InitMovDir() for elements that can move, although this is
2062 already done by InitField(); then, it checks the element that was at this
2063 field _before_ the call to InitField() (which can change it); lastly, it
2064 was not called for "mole with direction" elements, which were treated as
2065 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2071 static int get_key_element_from_nr(int key_nr)
2073 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2074 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2075 EL_EM_KEY_1 : EL_KEY_1);
2077 return key_base_element + key_nr;
2080 static int get_next_dropped_element(struct PlayerInfo *player)
2082 return (player->inventory_size > 0 ?
2083 player->inventory_element[player->inventory_size - 1] :
2084 player->inventory_infinite_element != EL_UNDEFINED ?
2085 player->inventory_infinite_element :
2086 player->dynabombs_left > 0 ?
2087 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2091 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2093 /* pos >= 0: get element from bottom of the stack;
2094 pos < 0: get element from top of the stack */
2098 int min_inventory_size = -pos;
2099 int inventory_pos = player->inventory_size - min_inventory_size;
2100 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2102 return (player->inventory_size >= min_inventory_size ?
2103 player->inventory_element[inventory_pos] :
2104 player->inventory_infinite_element != EL_UNDEFINED ?
2105 player->inventory_infinite_element :
2106 player->dynabombs_left >= min_dynabombs_left ?
2107 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112 int min_dynabombs_left = pos + 1;
2113 int min_inventory_size = pos + 1 - player->dynabombs_left;
2114 int inventory_pos = pos - player->dynabombs_left;
2116 return (player->inventory_infinite_element != EL_UNDEFINED ?
2117 player->inventory_infinite_element :
2118 player->dynabombs_left >= min_dynabombs_left ?
2119 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2120 player->inventory_size >= min_inventory_size ?
2121 player->inventory_element[inventory_pos] :
2126 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2128 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2129 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2132 if (gpo1->sort_priority != gpo2->sort_priority)
2133 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2135 compare_result = gpo1->nr - gpo2->nr;
2137 return compare_result;
2140 void InitGameControlValues()
2144 for (i = 0; game_panel_controls[i].nr != -1; i++)
2146 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2147 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2148 struct TextPosInfo *pos = gpc->pos;
2150 int type = gpc->type;
2154 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2155 Error(ERR_EXIT, "this should not happen -- please debug");
2158 /* force update of game controls after initialization */
2159 gpc->value = gpc->last_value = -1;
2160 gpc->frame = gpc->last_frame = -1;
2161 gpc->gfx_frame = -1;
2163 /* determine panel value width for later calculation of alignment */
2164 if (type == TYPE_INTEGER || type == TYPE_STRING)
2166 pos->width = pos->size * getFontWidth(pos->font);
2167 pos->height = getFontHeight(pos->font);
2169 else if (type == TYPE_ELEMENT)
2171 pos->width = pos->size;
2172 pos->height = pos->size;
2175 /* fill structure for game panel draw order */
2177 gpo->sort_priority = pos->sort_priority;
2180 /* sort game panel controls according to sort_priority and control number */
2181 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2182 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2185 void UpdatePlayfieldElementCount()
2187 boolean use_element_count = FALSE;
2190 /* first check if it is needed at all to calculate playfield element count */
2191 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2192 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2193 use_element_count = TRUE;
2195 if (!use_element_count)
2198 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2199 element_info[i].element_count = 0;
2201 SCAN_PLAYFIELD(x, y)
2203 element_info[Feld[x][y]].element_count++;
2206 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2207 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2208 if (IS_IN_GROUP(j, i))
2209 element_info[EL_GROUP_START + i].element_count +=
2210 element_info[j].element_count;
2213 void UpdateGameControlValues()
2216 int time = (local_player->LevelSolved ?
2217 local_player->LevelSolved_CountingTime :
2218 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2219 level.native_em_level->lev->time :
2220 level.time == 0 ? TimePlayed : TimeLeft);
2221 int score = (local_player->LevelSolved ?
2222 local_player->LevelSolved_CountingScore :
2223 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2224 level.native_em_level->lev->score :
2225 local_player->score);
2226 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2227 level.native_em_level->lev->required :
2228 local_player->gems_still_needed);
2229 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2230 level.native_em_level->lev->required > 0 :
2231 local_player->gems_still_needed > 0 ||
2232 local_player->sokobanfields_still_needed > 0 ||
2233 local_player->lights_still_needed > 0);
2235 UpdatePlayfieldElementCount();
2237 /* update game panel control values */
2239 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2240 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2242 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2243 for (i = 0; i < MAX_NUM_KEYS; i++)
2244 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2245 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2246 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2248 if (game.centered_player_nr == -1)
2250 for (i = 0; i < MAX_PLAYERS; i++)
2252 for (k = 0; k < MAX_NUM_KEYS; k++)
2254 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2256 if (level.native_em_level->ply[i]->keys & (1 << k))
2257 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2258 get_key_element_from_nr(k);
2260 else if (stored_player[i].key[k])
2261 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262 get_key_element_from_nr(k);
2265 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2266 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2267 level.native_em_level->ply[i]->dynamite;
2269 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2270 stored_player[i].inventory_size;
2272 if (stored_player[i].num_white_keys > 0)
2273 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2276 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2277 stored_player[i].num_white_keys;
2282 int player_nr = game.centered_player_nr;
2284 for (k = 0; k < MAX_NUM_KEYS; k++)
2286 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2289 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290 get_key_element_from_nr(k);
2292 else if (stored_player[player_nr].key[k])
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2297 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2298 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2299 level.native_em_level->ply[player_nr]->dynamite;
2301 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302 stored_player[player_nr].inventory_size;
2304 if (stored_player[player_nr].num_white_keys > 0)
2305 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2307 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2308 stored_player[player_nr].num_white_keys;
2311 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2313 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2314 get_inventory_element_from_pos(local_player, i);
2315 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2316 get_inventory_element_from_pos(local_player, -i - 1);
2319 game_panel_controls[GAME_PANEL_SCORE].value = score;
2320 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2322 game_panel_controls[GAME_PANEL_TIME].value = time;
2324 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2325 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2326 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2328 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2329 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2331 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2332 local_player->shield_normal_time_left;
2333 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2334 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2336 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2337 local_player->shield_deadly_time_left;
2339 game_panel_controls[GAME_PANEL_EXIT].value =
2340 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2342 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2343 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2344 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2345 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2346 EL_EMC_MAGIC_BALL_SWITCH);
2348 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2349 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2350 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2351 game.light_time_left;
2353 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2354 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2355 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2356 game.timegate_time_left;
2358 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2359 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2361 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2362 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2363 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2364 game.lenses_time_left;
2366 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2367 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2368 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2369 game.magnify_time_left;
2371 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2372 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2373 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2374 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2375 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2376 EL_BALLOON_SWITCH_NONE);
2378 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2379 local_player->dynabomb_count;
2380 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2381 local_player->dynabomb_size;
2382 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2383 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2385 game_panel_controls[GAME_PANEL_PENGUINS].value =
2386 local_player->friends_still_needed;
2388 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2389 local_player->sokobanfields_still_needed;
2390 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2391 local_player->sokobanfields_still_needed;
2393 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2394 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2396 for (i = 0; i < NUM_BELTS; i++)
2398 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2399 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2400 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2401 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2402 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2405 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2406 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2407 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2408 game.magic_wall_time_left;
2410 #if USE_PLAYER_GRAVITY
2411 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2412 local_player->gravity;
2414 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2417 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2418 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2420 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2421 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2422 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2423 game.panel.element[i].id : EL_UNDEFINED);
2425 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2426 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2427 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2428 element_info[game.panel.element_count[i].id].element_count : 0);
2430 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2431 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2432 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2433 element_info[game.panel.ce_score[i].id].collect_score : 0);
2435 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2436 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2437 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2438 element_info[game.panel.ce_score_element[i].id].collect_score :
2441 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2442 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2443 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2445 /* update game panel control frames */
2447 for (i = 0; game_panel_controls[i].nr != -1; i++)
2449 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2451 if (gpc->type == TYPE_ELEMENT)
2453 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2455 int last_anim_random_frame = gfx.anim_random_frame;
2456 int element = gpc->value;
2457 int graphic = el2panelimg(element);
2459 if (gpc->value != gpc->last_value)
2462 gpc->gfx_random = INIT_GFX_RANDOM();
2468 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2469 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2470 gpc->gfx_random = INIT_GFX_RANDOM();
2473 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2474 gfx.anim_random_frame = gpc->gfx_random;
2476 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2477 gpc->gfx_frame = element_info[element].collect_score;
2479 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2482 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2483 gfx.anim_random_frame = last_anim_random_frame;
2489 void DisplayGameControlValues()
2491 boolean redraw_panel = FALSE;
2494 for (i = 0; game_panel_controls[i].nr != -1; i++)
2496 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2498 if (PANEL_DEACTIVATED(gpc->pos))
2501 if (gpc->value == gpc->last_value &&
2502 gpc->frame == gpc->last_frame)
2505 redraw_panel = TRUE;
2511 /* copy default game door content to main double buffer */
2512 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2513 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2515 /* redraw game control buttons */
2517 RedrawGameButtons();
2523 game_status = GAME_MODE_PSEUDO_PANEL;
2526 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2528 for (i = 0; game_panel_controls[i].nr != -1; i++)
2532 int nr = game_panel_order[i].nr;
2533 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2535 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2538 struct TextPosInfo *pos = gpc->pos;
2539 int type = gpc->type;
2540 int value = gpc->value;
2541 int frame = gpc->frame;
2543 int last_value = gpc->last_value;
2544 int last_frame = gpc->last_frame;
2546 int size = pos->size;
2547 int font = pos->font;
2548 boolean draw_masked = pos->draw_masked;
2549 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2551 if (PANEL_DEACTIVATED(pos))
2555 if (value == last_value && frame == last_frame)
2559 gpc->last_value = value;
2560 gpc->last_frame = frame;
2563 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2566 if (type == TYPE_INTEGER)
2568 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2569 nr == GAME_PANEL_TIME)
2571 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2573 if (use_dynamic_size) /* use dynamic number of digits */
2575 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2576 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2577 int size2 = size1 + 1;
2578 int font1 = pos->font;
2579 int font2 = pos->font_alt;
2581 size = (value < value_change ? size1 : size2);
2582 font = (value < value_change ? font1 : font2);
2585 /* clear background if value just changed its size (dynamic digits) */
2586 if ((last_value < value_change) != (value < value_change))
2588 int width1 = size1 * getFontWidth(font1);
2589 int width2 = size2 * getFontWidth(font2);
2590 int max_width = MAX(width1, width2);
2591 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2593 pos->width = max_width;
2595 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2596 max_width, max_height);
2603 /* correct text size if "digits" is zero or less */
2605 size = strlen(int2str(value, size));
2607 /* dynamically correct text alignment */
2608 pos->width = size * getFontWidth(font);
2611 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2612 int2str(value, size), font, mask_mode);
2614 else if (type == TYPE_ELEMENT)
2616 int element, graphic;
2620 int dst_x = PANEL_XPOS(pos);
2621 int dst_y = PANEL_YPOS(pos);
2624 if (value != EL_UNDEFINED && value != EL_EMPTY)
2627 graphic = el2panelimg(value);
2629 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2632 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2636 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2639 width = graphic_info[graphic].width * size / TILESIZE;
2640 height = graphic_info[graphic].height * size / TILESIZE;
2644 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2645 dst_x - src_x, dst_y - src_y);
2646 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2651 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2656 if (value == EL_UNDEFINED || value == EL_EMPTY)
2658 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2659 graphic = el2panelimg(element);
2661 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2662 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2663 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2668 graphic = el2panelimg(value);
2670 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2673 width = graphic_info[graphic].width * size / TILESIZE;
2674 height = graphic_info[graphic].height * size / TILESIZE;
2676 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2679 else if (type == TYPE_STRING)
2681 boolean active = (value != 0);
2682 char *state_normal = "off";
2683 char *state_active = "on";
2684 char *state = (active ? state_active : state_normal);
2685 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2686 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2687 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2688 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2690 if (nr == GAME_PANEL_GRAVITY_STATE)
2692 int font1 = pos->font; /* (used for normal state) */
2693 int font2 = pos->font_alt; /* (used for active state) */
2695 int size1 = strlen(state_normal);
2696 int size2 = strlen(state_active);
2697 int width1 = size1 * getFontWidth(font1);
2698 int width2 = size2 * getFontWidth(font2);
2699 int max_width = MAX(width1, width2);
2700 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2702 pos->width = max_width;
2704 /* clear background for values that may have changed its size */
2705 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706 max_width, max_height);
2709 font = (active ? font2 : font1);
2719 /* don't truncate output if "chars" is zero or less */
2722 /* dynamically correct text alignment */
2723 pos->width = size * getFontWidth(font);
2727 s_cut = getStringCopyN(s, size);
2729 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2730 s_cut, font, mask_mode);
2736 redraw_mask |= REDRAW_DOOR_1;
2739 game_status = GAME_MODE_PLAYING;
2742 void UpdateAndDisplayGameControlValues()
2744 if (tape.warp_forward)
2747 UpdateGameControlValues();
2748 DisplayGameControlValues();
2751 void DrawGameValue_Emeralds(int value)
2753 struct TextPosInfo *pos = &game.panel.gems;
2755 int font_nr = pos->font;
2757 int font_nr = FONT_TEXT_2;
2759 int font_width = getFontWidth(font_nr);
2760 int chars = pos->size;
2763 return; /* !!! USE NEW STUFF !!! */
2766 if (PANEL_DEACTIVATED(pos))
2769 pos->width = chars * font_width;
2771 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2774 void DrawGameValue_Dynamite(int value)
2776 struct TextPosInfo *pos = &game.panel.inventory_count;
2778 int font_nr = pos->font;
2780 int font_nr = FONT_TEXT_2;
2782 int font_width = getFontWidth(font_nr);
2783 int chars = pos->size;
2786 return; /* !!! USE NEW STUFF !!! */
2789 if (PANEL_DEACTIVATED(pos))
2792 pos->width = chars * font_width;
2794 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2797 void DrawGameValue_Score(int value)
2799 struct TextPosInfo *pos = &game.panel.score;
2801 int font_nr = pos->font;
2803 int font_nr = FONT_TEXT_2;
2805 int font_width = getFontWidth(font_nr);
2806 int chars = pos->size;
2809 return; /* !!! USE NEW STUFF !!! */
2812 if (PANEL_DEACTIVATED(pos))
2815 pos->width = chars * font_width;
2817 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2820 void DrawGameValue_Time(int value)
2822 struct TextPosInfo *pos = &game.panel.time;
2823 static int last_value = -1;
2826 int chars = pos->size;
2828 int font1_nr = pos->font;
2829 int font2_nr = pos->font_alt;
2831 int font1_nr = FONT_TEXT_2;
2832 int font2_nr = FONT_TEXT_1;
2834 int font_nr = font1_nr;
2835 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2838 return; /* !!! USE NEW STUFF !!! */
2841 if (PANEL_DEACTIVATED(pos))
2844 if (use_dynamic_chars) /* use dynamic number of chars */
2846 chars = (value < 1000 ? chars1 : chars2);
2847 font_nr = (value < 1000 ? font1_nr : font2_nr);
2850 /* clear background if value just changed its size (dynamic chars only) */
2851 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2853 int width1 = chars1 * getFontWidth(font1_nr);
2854 int width2 = chars2 * getFontWidth(font2_nr);
2855 int max_width = MAX(width1, width2);
2856 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2858 pos->width = max_width;
2860 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2861 max_width, max_height);
2864 pos->width = chars * getFontWidth(font_nr);
2866 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2871 void DrawGameValue_Level(int value)
2873 struct TextPosInfo *pos = &game.panel.level_number;
2876 int chars = pos->size;
2878 int font1_nr = pos->font;
2879 int font2_nr = pos->font_alt;
2881 int font1_nr = FONT_TEXT_2;
2882 int font2_nr = FONT_TEXT_1;
2884 int font_nr = font1_nr;
2885 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2888 return; /* !!! USE NEW STUFF !!! */
2891 if (PANEL_DEACTIVATED(pos))
2894 if (use_dynamic_chars) /* use dynamic number of chars */
2896 chars = (level_nr < 100 ? chars1 : chars2);
2897 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2900 pos->width = chars * getFontWidth(font_nr);
2902 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2905 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2908 struct TextPosInfo *pos = &game.panel.keys;
2911 int base_key_graphic = EL_KEY_1;
2916 return; /* !!! USE NEW STUFF !!! */
2920 if (PANEL_DEACTIVATED(pos))
2925 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2926 base_key_graphic = EL_EM_KEY_1;
2930 pos->width = 4 * MINI_TILEX;
2934 for (i = 0; i < MAX_NUM_KEYS; i++)
2936 /* currently only 4 of 8 possible keys are displayed */
2937 for (i = 0; i < STD_NUM_KEYS; i++)
2941 struct TextPosInfo *pos = &game.panel.key[i];
2943 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2944 int src_y = DOOR_GFX_PAGEY1 + 123;
2946 int dst_x = PANEL_XPOS(pos);
2947 int dst_y = PANEL_YPOS(pos);
2949 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2950 int dst_y = PANEL_YPOS(pos);
2954 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2955 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2957 int graphic = el2edimg(element);
2961 if (PANEL_DEACTIVATED(pos))
2966 /* masked blit with tiles from half-size scaled bitmap does not work yet
2967 (no mask bitmap created for these sizes after loading and scaling) --
2968 solution: load without creating mask, scale, then create final mask */
2970 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2971 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2976 int graphic = el2edimg(base_key_graphic + i);
2981 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2983 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2984 dst_x - src_x, dst_y - src_y);
2985 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2991 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2993 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2994 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2997 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2999 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3000 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3008 void DrawGameValue_Emeralds(int value)
3010 int font_nr = FONT_TEXT_2;
3011 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3013 if (PANEL_DEACTIVATED(game.panel.gems))
3016 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3019 void DrawGameValue_Dynamite(int value)
3021 int font_nr = FONT_TEXT_2;
3022 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3024 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3027 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3030 void DrawGameValue_Score(int value)
3032 int font_nr = FONT_TEXT_2;
3033 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3035 if (PANEL_DEACTIVATED(game.panel.score))
3038 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3041 void DrawGameValue_Time(int value)
3043 int font1_nr = FONT_TEXT_2;
3045 int font2_nr = FONT_TEXT_1;
3047 int font2_nr = FONT_LEVEL_NUMBER;
3049 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3050 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3052 if (PANEL_DEACTIVATED(game.panel.time))
3055 /* clear background if value just changed its size */
3056 if (value == 999 || value == 1000)
3057 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3060 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3062 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3065 void DrawGameValue_Level(int value)
3067 int font1_nr = FONT_TEXT_2;
3069 int font2_nr = FONT_TEXT_1;
3071 int font2_nr = FONT_LEVEL_NUMBER;
3074 if (PANEL_DEACTIVATED(game.panel.level))
3078 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3080 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3083 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3085 int base_key_graphic = EL_KEY_1;
3088 if (PANEL_DEACTIVATED(game.panel.keys))
3091 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3092 base_key_graphic = EL_EM_KEY_1;
3094 /* currently only 4 of 8 possible keys are displayed */
3095 for (i = 0; i < STD_NUM_KEYS; i++)
3097 int x = XX_KEYS + i * MINI_TILEX;
3101 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3103 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3104 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3110 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3113 int key[MAX_NUM_KEYS];
3116 /* prevent EM engine from updating time/score values parallel to GameWon() */
3117 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3118 local_player->LevelSolved)
3121 for (i = 0; i < MAX_NUM_KEYS; i++)
3122 key[i] = key_bits & (1 << i);
3124 DrawGameValue_Level(level_nr);
3126 DrawGameValue_Emeralds(emeralds);
3127 DrawGameValue_Dynamite(dynamite);
3128 DrawGameValue_Score(score);
3129 DrawGameValue_Time(time);
3131 DrawGameValue_Keys(key);
3134 void UpdateGameDoorValues()
3136 UpdateGameControlValues();
3139 void DrawGameDoorValues()
3141 DisplayGameControlValues();
3144 void DrawGameDoorValues_OLD()
3146 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3147 int dynamite_value = 0;
3148 int score_value = (local_player->LevelSolved ? local_player->score_final :
3149 local_player->score);
3150 int gems_value = local_player->gems_still_needed;
3154 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3156 DrawGameDoorValues_EM();
3161 if (game.centered_player_nr == -1)
3163 for (i = 0; i < MAX_PLAYERS; i++)
3165 for (j = 0; j < MAX_NUM_KEYS; j++)
3166 if (stored_player[i].key[j])
3167 key_bits |= (1 << j);
3169 dynamite_value += stored_player[i].inventory_size;
3174 int player_nr = game.centered_player_nr;
3176 for (i = 0; i < MAX_NUM_KEYS; i++)
3177 if (stored_player[player_nr].key[i])
3178 key_bits |= (1 << i);
3180 dynamite_value = stored_player[player_nr].inventory_size;
3183 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3189 =============================================================================
3191 -----------------------------------------------------------------------------
3192 initialize game engine due to level / tape version number
3193 =============================================================================
3196 static void InitGameEngine()
3198 int i, j, k, l, x, y;
3200 /* set game engine from tape file when re-playing, else from level file */
3201 game.engine_version = (tape.playing ? tape.engine_version :
3202 level.game_version);
3204 /* ---------------------------------------------------------------------- */
3205 /* set flags for bugs and changes according to active game engine version */
3206 /* ---------------------------------------------------------------------- */
3209 Summary of bugfix/change:
3210 Fixed handling for custom elements that change when pushed by the player.
3212 Fixed/changed in version:
3216 Before 3.1.0, custom elements that "change when pushing" changed directly
3217 after the player started pushing them (until then handled in "DigField()").
3218 Since 3.1.0, these custom elements are not changed until the "pushing"
3219 move of the element is finished (now handled in "ContinueMoving()").
3221 Affected levels/tapes:
3222 The first condition is generally needed for all levels/tapes before version
3223 3.1.0, which might use the old behaviour before it was changed; known tapes
3224 that are affected are some tapes from the level set "Walpurgis Gardens" by
3226 The second condition is an exception from the above case and is needed for
3227 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3228 above (including some development versions of 3.1.0), but before it was
3229 known that this change would break tapes like the above and was fixed in
3230 3.1.1, so that the changed behaviour was active although the engine version
3231 while recording maybe was before 3.1.0. There is at least one tape that is
3232 affected by this exception, which is the tape for the one-level set "Bug
3233 Machine" by Juergen Bonhagen.
3236 game.use_change_when_pushing_bug =
3237 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3239 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3240 tape.game_version < VERSION_IDENT(3,1,1,0)));
3243 Summary of bugfix/change:
3244 Fixed handling for blocking the field the player leaves when moving.
3246 Fixed/changed in version:
3250 Before 3.1.1, when "block last field when moving" was enabled, the field
3251 the player is leaving when moving was blocked for the time of the move,
3252 and was directly unblocked afterwards. This resulted in the last field
3253 being blocked for exactly one less than the number of frames of one player
3254 move. Additionally, even when blocking was disabled, the last field was
3255 blocked for exactly one frame.
3256 Since 3.1.1, due to changes in player movement handling, the last field
3257 is not blocked at all when blocking is disabled. When blocking is enabled,
3258 the last field is blocked for exactly the number of frames of one player
3259 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3260 last field is blocked for exactly one more than the number of frames of
3263 Affected levels/tapes:
3264 (!!! yet to be determined -- probably many !!!)
3267 game.use_block_last_field_bug =
3268 (game.engine_version < VERSION_IDENT(3,1,1,0));
3271 Summary of bugfix/change:
3272 Changed behaviour of CE changes with multiple changes per single frame.
3274 Fixed/changed in version:
3278 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3279 This resulted in race conditions where CEs seem to behave strange in some
3280 situations (where triggered CE changes were just skipped because there was
3281 already a CE change on that tile in the playfield in that engine frame).
3282 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3283 (The number of changes per frame must be limited in any case, because else
3284 it is easily possible to define CE changes that would result in an infinite
3285 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3286 should be set large enough so that it would only be reached in cases where
3287 the corresponding CE change conditions run into a loop. Therefore, it seems
3288 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3289 maximal number of change pages for custom elements.)
3291 Affected levels/tapes:
3295 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3296 game.max_num_changes_per_frame = 1;
3298 game.max_num_changes_per_frame =
3299 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3302 /* ---------------------------------------------------------------------- */
3304 /* default scan direction: scan playfield from top/left to bottom/right */
3305 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3307 /* dynamically adjust element properties according to game engine version */
3308 InitElementPropertiesEngine(game.engine_version);
3311 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3312 printf(" tape version == %06d [%s] [file: %06d]\n",
3313 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3315 printf(" => game.engine_version == %06d\n", game.engine_version);
3318 /* ---------- initialize player's initial move delay --------------------- */
3320 /* dynamically adjust player properties according to level information */
3321 for (i = 0; i < MAX_PLAYERS; i++)
3322 game.initial_move_delay_value[i] =
3323 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3325 /* dynamically adjust player properties according to game engine version */
3326 for (i = 0; i < MAX_PLAYERS; i++)
3327 game.initial_move_delay[i] =
3328 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3329 game.initial_move_delay_value[i] : 0);
3331 /* ---------- initialize player's initial push delay --------------------- */
3333 /* dynamically adjust player properties according to game engine version */
3334 game.initial_push_delay_value =
3335 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3337 /* ---------- initialize changing elements ------------------------------- */
3339 /* initialize changing elements information */
3340 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3342 struct ElementInfo *ei = &element_info[i];
3344 /* this pointer might have been changed in the level editor */
3345 ei->change = &ei->change_page[0];
3347 if (!IS_CUSTOM_ELEMENT(i))
3349 ei->change->target_element = EL_EMPTY_SPACE;
3350 ei->change->delay_fixed = 0;
3351 ei->change->delay_random = 0;
3352 ei->change->delay_frames = 1;
3355 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357 ei->has_change_event[j] = FALSE;
3359 ei->event_page_nr[j] = 0;
3360 ei->event_page[j] = &ei->change_page[0];
3364 /* add changing elements from pre-defined list */
3365 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3367 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3368 struct ElementInfo *ei = &element_info[ch_delay->element];
3370 ei->change->target_element = ch_delay->target_element;
3371 ei->change->delay_fixed = ch_delay->change_delay;
3373 ei->change->pre_change_function = ch_delay->pre_change_function;
3374 ei->change->change_function = ch_delay->change_function;
3375 ei->change->post_change_function = ch_delay->post_change_function;
3377 ei->change->can_change = TRUE;
3378 ei->change->can_change_or_has_action = TRUE;
3380 ei->has_change_event[CE_DELAY] = TRUE;
3382 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3383 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3386 /* ---------- initialize internal run-time variables --------------------- */
3388 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3390 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3392 for (j = 0; j < ei->num_change_pages; j++)
3394 ei->change_page[j].can_change_or_has_action =
3395 (ei->change_page[j].can_change |
3396 ei->change_page[j].has_action);
3400 /* add change events from custom element configuration */
3401 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3403 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3405 for (j = 0; j < ei->num_change_pages; j++)
3407 if (!ei->change_page[j].can_change_or_has_action)
3410 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3412 /* only add event page for the first page found with this event */
3413 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3415 ei->has_change_event[k] = TRUE;
3417 ei->event_page_nr[k] = j;
3418 ei->event_page[k] = &ei->change_page[j];
3425 /* ---------- initialize reference elements in change conditions --------- */
3427 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3429 int element = EL_CUSTOM_START + i;
3430 struct ElementInfo *ei = &element_info[element];
3432 for (j = 0; j < ei->num_change_pages; j++)
3434 int trigger_element = ei->change_page[j].initial_trigger_element;
3436 if (trigger_element >= EL_PREV_CE_8 &&
3437 trigger_element <= EL_NEXT_CE_8)
3438 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3440 ei->change_page[j].trigger_element = trigger_element;
3445 /* ---------- initialize run-time trigger player and element ------------- */
3447 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3449 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3451 for (j = 0; j < ei->num_change_pages; j++)
3453 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3454 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3455 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3456 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3457 ei->change_page[j].actual_trigger_ce_value = 0;
3458 ei->change_page[j].actual_trigger_ce_score = 0;
3462 /* ---------- initialize trigger events ---------------------------------- */
3464 /* initialize trigger events information */
3465 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3466 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3467 trigger_events[i][j] = FALSE;
3469 /* add trigger events from element change event properties */
3470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3472 struct ElementInfo *ei = &element_info[i];
3474 for (j = 0; j < ei->num_change_pages; j++)
3476 if (!ei->change_page[j].can_change_or_has_action)
3479 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3481 int trigger_element = ei->change_page[j].trigger_element;
3483 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3485 if (ei->change_page[j].has_event[k])
3487 if (IS_GROUP_ELEMENT(trigger_element))
3489 struct ElementGroupInfo *group =
3490 element_info[trigger_element].group;
3492 for (l = 0; l < group->num_elements_resolved; l++)
3493 trigger_events[group->element_resolved[l]][k] = TRUE;
3495 else if (trigger_element == EL_ANY_ELEMENT)
3496 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3497 trigger_events[l][k] = TRUE;
3499 trigger_events[trigger_element][k] = TRUE;
3506 /* ---------- initialize push delay -------------------------------------- */
3508 /* initialize push delay values to default */
3509 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3511 if (!IS_CUSTOM_ELEMENT(i))
3513 /* set default push delay values (corrected since version 3.0.7-1) */
3514 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3516 element_info[i].push_delay_fixed = 2;
3517 element_info[i].push_delay_random = 8;
3521 element_info[i].push_delay_fixed = 8;
3522 element_info[i].push_delay_random = 8;
3527 /* set push delay value for certain elements from pre-defined list */
3528 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3530 int e = push_delay_list[i].element;
3532 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3533 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3536 /* set push delay value for Supaplex elements for newer engine versions */
3537 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3539 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3541 if (IS_SP_ELEMENT(i))
3543 /* set SP push delay to just enough to push under a falling zonk */
3544 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3546 element_info[i].push_delay_fixed = delay;
3547 element_info[i].push_delay_random = 0;
3552 /* ---------- initialize move stepsize ----------------------------------- */
3554 /* initialize move stepsize values to default */
3555 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3556 if (!IS_CUSTOM_ELEMENT(i))
3557 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3559 /* set move stepsize value for certain elements from pre-defined list */
3560 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3562 int e = move_stepsize_list[i].element;
3564 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3567 /* ---------- initialize collect score ----------------------------------- */
3569 /* initialize collect score values for custom elements from initial value */
3570 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3571 if (IS_CUSTOM_ELEMENT(i))
3572 element_info[i].collect_score = element_info[i].collect_score_initial;
3574 /* ---------- initialize collect count ----------------------------------- */
3576 /* initialize collect count values for non-custom elements */
3577 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3578 if (!IS_CUSTOM_ELEMENT(i))
3579 element_info[i].collect_count_initial = 0;
3581 /* add collect count values for all elements from pre-defined list */
3582 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3583 element_info[collect_count_list[i].element].collect_count_initial =
3584 collect_count_list[i].count;
3586 /* ---------- initialize access direction -------------------------------- */
3588 /* initialize access direction values to default (access from every side) */
3589 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3590 if (!IS_CUSTOM_ELEMENT(i))
3591 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3593 /* set access direction value for certain elements from pre-defined list */
3594 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3595 element_info[access_direction_list[i].element].access_direction =
3596 access_direction_list[i].direction;
3598 /* ---------- initialize explosion content ------------------------------- */
3599 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601 if (IS_CUSTOM_ELEMENT(i))
3604 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3606 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3608 element_info[i].content.e[x][y] =
3609 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3610 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3611 i == EL_PLAYER_3 ? EL_EMERALD :
3612 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3613 i == EL_MOLE ? EL_EMERALD_RED :
3614 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3615 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3616 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3617 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3618 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3619 i == EL_WALL_EMERALD ? EL_EMERALD :
3620 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3621 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3622 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3623 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3624 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3625 i == EL_WALL_PEARL ? EL_PEARL :
3626 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3631 /* ---------- initialize recursion detection ------------------------------ */
3632 recursion_loop_depth = 0;
3633 recursion_loop_detected = FALSE;
3634 recursion_loop_element = EL_UNDEFINED;
3636 /* ---------- initialize graphics engine ---------------------------------- */
3637 game.scroll_delay_value =
3638 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3639 setup.scroll_delay ? setup.scroll_delay_value : 0);
3640 game.scroll_delay_value =
3641 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3644 int get_num_special_action(int element, int action_first, int action_last)
3646 int num_special_action = 0;
3649 for (i = action_first; i <= action_last; i++)
3651 boolean found = FALSE;
3653 for (j = 0; j < NUM_DIRECTIONS; j++)
3654 if (el_act_dir2img(element, i, j) !=
3655 el_act_dir2img(element, ACTION_DEFAULT, j))
3659 num_special_action++;
3664 return num_special_action;
3669 =============================================================================
3671 -----------------------------------------------------------------------------
3672 initialize and start new game
3673 =============================================================================
3678 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3679 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3680 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3682 boolean do_fading = (game_status == GAME_MODE_MAIN);
3685 int initial_move_dir = MV_DOWN;
3687 int initial_move_dir = MV_NONE;
3691 game_status = GAME_MODE_PLAYING;
3694 InitGameControlValues();
3696 /* don't play tapes over network */
3697 network_playing = (options.network && !tape.playing);
3699 for (i = 0; i < MAX_PLAYERS; i++)
3701 struct PlayerInfo *player = &stored_player[i];
3703 player->index_nr = i;
3704 player->index_bit = (1 << i);
3705 player->element_nr = EL_PLAYER_1 + i;
3707 player->present = FALSE;
3708 player->active = FALSE;
3709 player->killed = FALSE;
3712 player->effective_action = 0;
3713 player->programmed_action = 0;
3716 player->score_final = 0;
3718 player->gems_still_needed = level.gems_needed;
3719 player->sokobanfields_still_needed = 0;
3720 player->lights_still_needed = 0;
3721 player->friends_still_needed = 0;
3723 for (j = 0; j < MAX_NUM_KEYS; j++)
3724 player->key[j] = FALSE;
3726 player->num_white_keys = 0;
3728 player->dynabomb_count = 0;
3729 player->dynabomb_size = 1;
3730 player->dynabombs_left = 0;
3731 player->dynabomb_xl = FALSE;
3733 player->MovDir = initial_move_dir;
3736 player->GfxDir = initial_move_dir;
3737 player->GfxAction = ACTION_DEFAULT;
3739 player->StepFrame = 0;
3741 player->initial_element = player->element_nr;
3742 player->artwork_element =
3743 (level.use_artwork_element[i] ? level.artwork_element[i] :
3744 player->element_nr);
3745 player->use_murphy = FALSE;
3747 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3748 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3750 player->gravity = level.initial_player_gravity[i];
3752 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3754 player->actual_frame_counter = 0;
3756 player->step_counter = 0;
3758 player->last_move_dir = initial_move_dir;
3760 player->is_active = FALSE;
3762 player->is_waiting = FALSE;
3763 player->is_moving = FALSE;
3764 player->is_auto_moving = FALSE;
3765 player->is_digging = FALSE;
3766 player->is_snapping = FALSE;
3767 player->is_collecting = FALSE;
3768 player->is_pushing = FALSE;
3769 player->is_switching = FALSE;
3770 player->is_dropping = FALSE;
3771 player->is_dropping_pressed = FALSE;
3773 player->is_bored = FALSE;
3774 player->is_sleeping = FALSE;
3776 player->frame_counter_bored = -1;
3777 player->frame_counter_sleeping = -1;
3779 player->anim_delay_counter = 0;
3780 player->post_delay_counter = 0;
3782 player->dir_waiting = initial_move_dir;
3783 player->action_waiting = ACTION_DEFAULT;
3784 player->last_action_waiting = ACTION_DEFAULT;
3785 player->special_action_bored = ACTION_DEFAULT;
3786 player->special_action_sleeping = ACTION_DEFAULT;
3788 player->switch_x = -1;
3789 player->switch_y = -1;
3791 player->drop_x = -1;
3792 player->drop_y = -1;
3794 player->show_envelope = 0;
3796 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3798 player->push_delay = -1; /* initialized when pushing starts */
3799 player->push_delay_value = game.initial_push_delay_value;
3801 player->drop_delay = 0;
3802 player->drop_pressed_delay = 0;
3804 player->last_jx = -1;
3805 player->last_jy = -1;
3809 player->shield_normal_time_left = 0;
3810 player->shield_deadly_time_left = 0;
3812 player->inventory_infinite_element = EL_UNDEFINED;
3813 player->inventory_size = 0;
3815 if (level.use_initial_inventory[i])
3817 for (j = 0; j < level.initial_inventory_size[i]; j++)
3819 int element = level.initial_inventory_content[i][j];
3820 int collect_count = element_info[element].collect_count_initial;
3823 if (!IS_CUSTOM_ELEMENT(element))
3826 if (collect_count == 0)
3827 player->inventory_infinite_element = element;
3829 for (k = 0; k < collect_count; k++)
3830 if (player->inventory_size < MAX_INVENTORY_SIZE)
3831 player->inventory_element[player->inventory_size++] = element;
3835 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3836 SnapField(player, 0, 0);
3838 player->LevelSolved = FALSE;
3839 player->GameOver = FALSE;
3841 player->LevelSolved_GameWon = FALSE;
3842 player->LevelSolved_GameEnd = FALSE;
3843 player->LevelSolved_PanelOff = FALSE;
3844 player->LevelSolved_SaveTape = FALSE;
3845 player->LevelSolved_SaveScore = FALSE;
3846 player->LevelSolved_CountingTime = 0;
3847 player->LevelSolved_CountingScore = 0;
3850 network_player_action_received = FALSE;
3852 #if defined(NETWORK_AVALIABLE)
3853 /* initial null action */
3854 if (network_playing)
3855 SendToServer_MovePlayer(MV_NONE);
3864 TimeLeft = level.time;
3867 ScreenMovDir = MV_NONE;
3871 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3873 AllPlayersGone = FALSE;
3875 game.yamyam_content_nr = 0;
3876 game.robot_wheel_active = FALSE;
3877 game.magic_wall_active = FALSE;
3878 game.magic_wall_time_left = 0;
3879 game.light_time_left = 0;
3880 game.timegate_time_left = 0;
3881 game.switchgate_pos = 0;
3882 game.wind_direction = level.wind_direction_initial;
3884 #if !USE_PLAYER_GRAVITY
3885 game.gravity = FALSE;
3886 game.explosions_delayed = TRUE;
3889 game.lenses_time_left = 0;
3890 game.magnify_time_left = 0;
3892 game.ball_state = level.ball_state_initial;
3893 game.ball_content_nr = 0;
3895 game.envelope_active = FALSE;
3897 /* set focus to local player for network games, else to all players */
3898 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3899 game.centered_player_nr_next = game.centered_player_nr;
3900 game.set_centered_player = FALSE;
3902 if (network_playing && tape.recording)
3904 /* store client dependent player focus when recording network games */
3905 tape.centered_player_nr_next = game.centered_player_nr_next;
3906 tape.set_centered_player = TRUE;
3909 for (i = 0; i < NUM_BELTS; i++)
3911 game.belt_dir[i] = MV_NONE;
3912 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3915 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3916 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3918 SCAN_PLAYFIELD(x, y)
3920 Feld[x][y] = level.field[x][y];
3921 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3922 ChangeDelay[x][y] = 0;
3923 ChangePage[x][y] = -1;
3924 #if USE_NEW_CUSTOM_VALUE
3925 CustomValue[x][y] = 0; /* initialized in InitField() */
3927 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3929 WasJustMoving[x][y] = 0;
3930 WasJustFalling[x][y] = 0;
3931 CheckCollision[x][y] = 0;
3932 CheckImpact[x][y] = 0;
3934 Pushed[x][y] = FALSE;
3936 ChangeCount[x][y] = 0;
3937 ChangeEvent[x][y] = -1;
3939 ExplodePhase[x][y] = 0;
3940 ExplodeDelay[x][y] = 0;
3941 ExplodeField[x][y] = EX_TYPE_NONE;
3943 RunnerVisit[x][y] = 0;
3944 PlayerVisit[x][y] = 0;
3947 GfxRandom[x][y] = INIT_GFX_RANDOM();
3948 GfxElement[x][y] = EL_UNDEFINED;
3949 GfxAction[x][y] = ACTION_DEFAULT;
3950 GfxDir[x][y] = MV_NONE;
3951 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3954 SCAN_PLAYFIELD(x, y)
3956 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3958 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3960 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3963 InitField(x, y, TRUE);
3965 ResetGfxAnimation(x, y);
3970 for (i = 0; i < MAX_PLAYERS; i++)
3972 struct PlayerInfo *player = &stored_player[i];
3974 /* set number of special actions for bored and sleeping animation */
3975 player->num_special_action_bored =
3976 get_num_special_action(player->artwork_element,
3977 ACTION_BORING_1, ACTION_BORING_LAST);
3978 player->num_special_action_sleeping =
3979 get_num_special_action(player->artwork_element,
3980 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3983 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3984 emulate_sb ? EMU_SOKOBAN :
3985 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3987 #if USE_NEW_ALL_SLIPPERY
3988 /* initialize type of slippery elements */
3989 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3991 if (!IS_CUSTOM_ELEMENT(i))
3993 /* default: elements slip down either to the left or right randomly */
3994 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3996 /* SP style elements prefer to slip down on the left side */
3997 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3998 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4000 /* BD style elements prefer to slip down on the left side */
4001 if (game.emulation == EMU_BOULDERDASH)
4002 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4007 /* initialize explosion and ignition delay */
4008 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4010 if (!IS_CUSTOM_ELEMENT(i))
4013 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4014 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4015 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4016 int last_phase = (num_phase + 1) * delay;
4017 int half_phase = (num_phase / 2) * delay;
4019 element_info[i].explosion_delay = last_phase - 1;
4020 element_info[i].ignition_delay = half_phase;
4022 if (i == EL_BLACK_ORB)
4023 element_info[i].ignition_delay = 1;
4027 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4028 element_info[i].explosion_delay = 1;
4030 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4031 element_info[i].ignition_delay = 1;
4035 /* correct non-moving belts to start moving left */
4036 for (i = 0; i < NUM_BELTS; i++)
4037 if (game.belt_dir[i] == MV_NONE)
4038 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4040 /* check if any connected player was not found in playfield */
4041 for (i = 0; i < MAX_PLAYERS; i++)
4043 struct PlayerInfo *player = &stored_player[i];
4045 if (player->connected && !player->present)
4047 for (j = 0; j < MAX_PLAYERS; j++)
4049 struct PlayerInfo *some_player = &stored_player[j];
4050 int jx = some_player->jx, jy = some_player->jy;
4052 /* assign first free player found that is present in the playfield */
4053 if (some_player->present && !some_player->connected)
4055 player->present = TRUE;
4056 player->active = TRUE;
4058 some_player->present = FALSE;
4059 some_player->active = FALSE;
4061 player->initial_element = some_player->initial_element;
4062 player->artwork_element = some_player->artwork_element;
4064 player->block_last_field = some_player->block_last_field;
4065 player->block_delay_adjustment = some_player->block_delay_adjustment;
4067 StorePlayer[jx][jy] = player->element_nr;
4068 player->jx = player->last_jx = jx;
4069 player->jy = player->last_jy = jy;
4079 /* when playing a tape, eliminate all players who do not participate */
4081 for (i = 0; i < MAX_PLAYERS; i++)
4083 if (stored_player[i].active && !tape.player_participates[i])
4085 struct PlayerInfo *player = &stored_player[i];
4086 int jx = player->jx, jy = player->jy;
4088 player->active = FALSE;
4089 StorePlayer[jx][jy] = 0;
4090 Feld[jx][jy] = EL_EMPTY;
4094 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4096 /* when in single player mode, eliminate all but the first active player */
4098 for (i = 0; i < MAX_PLAYERS; i++)
4100 if (stored_player[i].active)
4102 for (j = i + 1; j < MAX_PLAYERS; j++)
4104 if (stored_player[j].active)
4106 struct PlayerInfo *player = &stored_player[j];
4107 int jx = player->jx, jy = player->jy;
4109 player->active = FALSE;
4110 player->present = FALSE;
4112 StorePlayer[jx][jy] = 0;
4113 Feld[jx][jy] = EL_EMPTY;
4120 /* when recording the game, store which players take part in the game */
4123 for (i = 0; i < MAX_PLAYERS; i++)
4124 if (stored_player[i].active)
4125 tape.player_participates[i] = TRUE;
4130 for (i = 0; i < MAX_PLAYERS; i++)
4132 struct PlayerInfo *player = &stored_player[i];
4134 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4139 if (local_player == player)
4140 printf("Player %d is local player.\n", i+1);
4144 if (BorderElement == EL_EMPTY)
4147 SBX_Right = lev_fieldx - SCR_FIELDX;
4149 SBY_Lower = lev_fieldy - SCR_FIELDY;
4154 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4156 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4159 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4162 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4165 /* if local player not found, look for custom element that might create
4166 the player (make some assumptions about the right custom element) */
4167 if (!local_player->present)
4169 int start_x = 0, start_y = 0;
4170 int found_rating = 0;
4171 int found_element = EL_UNDEFINED;
4172 int player_nr = local_player->index_nr;
4174 SCAN_PLAYFIELD(x, y)
4176 int element = Feld[x][y];
4181 if (level.use_start_element[player_nr] &&
4182 level.start_element[player_nr] == element &&
4189 found_element = element;
4192 if (!IS_CUSTOM_ELEMENT(element))
4195 if (CAN_CHANGE(element))
4197 for (i = 0; i < element_info[element].num_change_pages; i++)
4199 /* check for player created from custom element as single target */
4200 content = element_info[element].change_page[i].target_element;
4201 is_player = ELEM_IS_PLAYER(content);
4203 if (is_player && (found_rating < 3 ||
4204 (found_rating == 3 && element < found_element)))
4210 found_element = element;
4215 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4217 /* check for player created from custom element as explosion content */
4218 content = element_info[element].content.e[xx][yy];
4219 is_player = ELEM_IS_PLAYER(content);
4221 if (is_player && (found_rating < 2 ||
4222 (found_rating == 2 && element < found_element)))
4224 start_x = x + xx - 1;
4225 start_y = y + yy - 1;
4228 found_element = element;
4231 if (!CAN_CHANGE(element))
4234 for (i = 0; i < element_info[element].num_change_pages; i++)
4236 /* check for player created from custom element as extended target */
4238 element_info[element].change_page[i].target_content.e[xx][yy];
4240 is_player = ELEM_IS_PLAYER(content);
4242 if (is_player && (found_rating < 1 ||
4243 (found_rating == 1 && element < found_element)))
4245 start_x = x + xx - 1;
4246 start_y = y + yy - 1;
4249 found_element = element;
4255 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4256 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4259 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4260 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4265 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4266 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4267 local_player->jx - MIDPOSX);
4269 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4270 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4271 local_player->jy - MIDPOSY);
4275 /* do not use PLAYING mask for fading out from main screen */
4276 game_status = GAME_MODE_MAIN;
4281 if (!game.restart_level)
4282 CloseDoor(DOOR_CLOSE_1);
4285 if (level_editor_test_game)
4286 FadeSkipNextFadeIn();
4288 FadeSetEnterScreen();
4290 if (level_editor_test_game)
4291 fading = fading_none;
4293 fading = menu.destination;
4297 FadeOut(REDRAW_FIELD);
4300 FadeOut(REDRAW_FIELD);
4304 game_status = GAME_MODE_PLAYING;
4307 /* !!! FIX THIS (START) !!! */
4308 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4310 InitGameEngine_EM();
4312 /* blit playfield from scroll buffer to normal back buffer for fading in */
4313 BlitScreenToBitmap_EM(backbuffer);
4320 /* after drawing the level, correct some elements */
4321 if (game.timegate_time_left == 0)
4322 CloseAllOpenTimegates();
4324 /* blit playfield from scroll buffer to normal back buffer for fading in */
4325 if (setup.soft_scrolling)
4326 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4328 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4330 /* !!! FIX THIS (END) !!! */
4333 FadeIn(REDRAW_FIELD);
4336 FadeIn(REDRAW_FIELD);
4341 if (!game.restart_level)
4343 /* copy default game door content to main double buffer */
4344 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4345 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4348 SetPanelBackground();
4349 SetDrawBackgroundMask(REDRAW_DOOR_1);
4352 UpdateAndDisplayGameControlValues();
4354 UpdateGameDoorValues();
4355 DrawGameDoorValues();
4358 if (!game.restart_level)
4362 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4363 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4364 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4368 /* copy actual game door content to door double buffer for OpenDoor() */
4369 BlitBitmap(drawto, bitmap_db_door,
4370 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4372 OpenDoor(DOOR_OPEN_ALL);
4374 PlaySound(SND_GAME_STARTING);
4376 if (setup.sound_music)
4379 KeyboardAutoRepeatOffUnlessAutoplay();
4383 for (i = 0; i < MAX_PLAYERS; i++)
4384 printf("Player %d %sactive.\n",
4385 i + 1, (stored_player[i].active ? "" : "not "));
4396 game.restart_level = FALSE;
4399 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4401 /* this is used for non-R'n'D game engines to update certain engine values */
4403 /* needed to determine if sounds are played within the visible screen area */
4404 scroll_x = actual_scroll_x;
4405 scroll_y = actual_scroll_y;
4408 void InitMovDir(int x, int y)
4410 int i, element = Feld[x][y];
4411 static int xy[4][2] =
4418 static int direction[3][4] =
4420 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4421 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4422 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4431 Feld[x][y] = EL_BUG;
4432 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4435 case EL_SPACESHIP_RIGHT:
4436 case EL_SPACESHIP_UP:
4437 case EL_SPACESHIP_LEFT:
4438 case EL_SPACESHIP_DOWN:
4439 Feld[x][y] = EL_SPACESHIP;
4440 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4443 case EL_BD_BUTTERFLY_RIGHT:
4444 case EL_BD_BUTTERFLY_UP:
4445 case EL_BD_BUTTERFLY_LEFT:
4446 case EL_BD_BUTTERFLY_DOWN:
4447 Feld[x][y] = EL_BD_BUTTERFLY;
4448 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4451 case EL_BD_FIREFLY_RIGHT:
4452 case EL_BD_FIREFLY_UP:
4453 case EL_BD_FIREFLY_LEFT:
4454 case EL_BD_FIREFLY_DOWN:
4455 Feld[x][y] = EL_BD_FIREFLY;
4456 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4459 case EL_PACMAN_RIGHT:
4461 case EL_PACMAN_LEFT:
4462 case EL_PACMAN_DOWN:
4463 Feld[x][y] = EL_PACMAN;
4464 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4467 case EL_YAMYAM_LEFT:
4468 case EL_YAMYAM_RIGHT:
4470 case EL_YAMYAM_DOWN:
4471 Feld[x][y] = EL_YAMYAM;
4472 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4475 case EL_SP_SNIKSNAK:
4476 MovDir[x][y] = MV_UP;
4479 case EL_SP_ELECTRON:
4480 MovDir[x][y] = MV_LEFT;
4487 Feld[x][y] = EL_MOLE;
4488 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4492 if (IS_CUSTOM_ELEMENT(element))
4494 struct ElementInfo *ei = &element_info[element];
4495 int move_direction_initial = ei->move_direction_initial;
4496 int move_pattern = ei->move_pattern;
4498 if (move_direction_initial == MV_START_PREVIOUS)
4500 if (MovDir[x][y] != MV_NONE)
4503 move_direction_initial = MV_START_AUTOMATIC;
4506 if (move_direction_initial == MV_START_RANDOM)
4507 MovDir[x][y] = 1 << RND(4);
4508 else if (move_direction_initial & MV_ANY_DIRECTION)
4509 MovDir[x][y] = move_direction_initial;
4510 else if (move_pattern == MV_ALL_DIRECTIONS ||
4511 move_pattern == MV_TURNING_LEFT ||
4512 move_pattern == MV_TURNING_RIGHT ||
4513 move_pattern == MV_TURNING_LEFT_RIGHT ||
4514 move_pattern == MV_TURNING_RIGHT_LEFT ||
4515 move_pattern == MV_TURNING_RANDOM)
4516 MovDir[x][y] = 1 << RND(4);
4517 else if (move_pattern == MV_HORIZONTAL)
4518 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4519 else if (move_pattern == MV_VERTICAL)
4520 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4521 else if (move_pattern & MV_ANY_DIRECTION)
4522 MovDir[x][y] = element_info[element].move_pattern;
4523 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4524 move_pattern == MV_ALONG_RIGHT_SIDE)
4526 /* use random direction as default start direction */
4527 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4528 MovDir[x][y] = 1 << RND(4);
4530 for (i = 0; i < NUM_DIRECTIONS; i++)
4532 int x1 = x + xy[i][0];
4533 int y1 = y + xy[i][1];
4535 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4537 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4538 MovDir[x][y] = direction[0][i];
4540 MovDir[x][y] = direction[1][i];
4549 MovDir[x][y] = 1 << RND(4);
4551 if (element != EL_BUG &&
4552 element != EL_SPACESHIP &&
4553 element != EL_BD_BUTTERFLY &&
4554 element != EL_BD_FIREFLY)
4557 for (i = 0; i < NUM_DIRECTIONS; i++)
4559 int x1 = x + xy[i][0];
4560 int y1 = y + xy[i][1];
4562 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4564 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4566 MovDir[x][y] = direction[0][i];
4569 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4570 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4572 MovDir[x][y] = direction[1][i];
4581 GfxDir[x][y] = MovDir[x][y];
4584 void InitAmoebaNr(int x, int y)
4587 int group_nr = AmoebeNachbarNr(x, y);
4591 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4593 if (AmoebaCnt[i] == 0)
4601 AmoebaNr[x][y] = group_nr;
4602 AmoebaCnt[group_nr]++;
4603 AmoebaCnt2[group_nr]++;
4606 static void PlayerWins(struct PlayerInfo *player)
4608 player->LevelSolved = TRUE;
4609 player->GameOver = TRUE;
4611 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4612 level.native_em_level->lev->score : player->score);
4614 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4615 player->LevelSolved_CountingScore = player->score_final;
4620 static int time, time_final;
4621 static int score, score_final;
4622 static int game_over_delay_1 = 0;
4623 static int game_over_delay_2 = 0;
4624 int game_over_delay_value_1 = 50;
4625 int game_over_delay_value_2 = 50;
4627 if (!local_player->LevelSolved_GameWon)
4631 /* do not start end game actions before the player stops moving (to exit) */
4632 if (local_player->MovPos)
4635 local_player->LevelSolved_GameWon = TRUE;
4636 local_player->LevelSolved_SaveTape = tape.recording;
4637 local_player->LevelSolved_SaveScore = !tape.playing;
4639 if (tape.auto_play) /* tape might already be stopped here */
4640 tape.auto_play_level_solved = TRUE;
4646 game_over_delay_1 = game_over_delay_value_1;
4647 game_over_delay_2 = game_over_delay_value_2;
4649 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4650 score = score_final = local_player->score_final;
4655 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4657 else if (level.time == 0 && TimePlayed < 999)
4660 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4663 local_player->score_final = score_final;
4665 if (level_editor_test_game)
4668 score = score_final;
4671 local_player->LevelSolved_CountingTime = time;
4672 local_player->LevelSolved_CountingScore = score;
4674 game_panel_controls[GAME_PANEL_TIME].value = time;
4675 game_panel_controls[GAME_PANEL_SCORE].value = score;
4677 DisplayGameControlValues();
4679 DrawGameValue_Time(time);
4680 DrawGameValue_Score(score);
4684 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4686 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4688 /* close exit door after last player */
4689 if ((AllPlayersGone &&
4690 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4691 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4692 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4693 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4694 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4696 int element = Feld[ExitX][ExitY];
4699 if (element == EL_EM_EXIT_OPEN ||
4700 element == EL_EM_STEEL_EXIT_OPEN)
4707 Feld[ExitX][ExitY] =
4708 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4709 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4710 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4711 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4712 EL_EM_STEEL_EXIT_CLOSING);
4714 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4718 /* player disappears */
4719 DrawLevelField(ExitX, ExitY);
4722 for (i = 0; i < MAX_PLAYERS; i++)
4724 struct PlayerInfo *player = &stored_player[i];
4726 if (player->present)
4728 RemovePlayer(player);
4730 /* player disappears */
4731 DrawLevelField(player->jx, player->jy);
4736 PlaySound(SND_GAME_WINNING);
4739 if (game_over_delay_1 > 0)
4741 game_over_delay_1--;
4746 if (time != time_final)
4748 int time_to_go = ABS(time_final - time);
4749 int time_count_dir = (time < time_final ? +1 : -1);
4750 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4752 time += time_count_steps * time_count_dir;
4753 score += time_count_steps * level.score[SC_TIME_BONUS];
4756 local_player->LevelSolved_CountingTime = time;
4757 local_player->LevelSolved_CountingScore = score;
4759 game_panel_controls[GAME_PANEL_TIME].value = time;
4760 game_panel_controls[GAME_PANEL_SCORE].value = score;
4762 DisplayGameControlValues();
4764 DrawGameValue_Time(time);
4765 DrawGameValue_Score(score);
4768 if (time == time_final)
4769 StopSound(SND_GAME_LEVELTIME_BONUS);
4770 else if (setup.sound_loops)
4771 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4773 PlaySound(SND_GAME_LEVELTIME_BONUS);
4778 local_player->LevelSolved_PanelOff = TRUE;
4780 if (game_over_delay_2 > 0)
4782 game_over_delay_2--;
4795 boolean raise_level = FALSE;
4797 local_player->LevelSolved_GameEnd = TRUE;
4799 CloseDoor(DOOR_CLOSE_1);
4801 if (local_player->LevelSolved_SaveTape)
4808 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4810 SaveTape(tape.level_nr); /* ask to save tape */
4814 if (level_editor_test_game)
4816 game_status = GAME_MODE_MAIN;
4819 DrawAndFadeInMainMenu(REDRAW_FIELD);
4827 if (!local_player->LevelSolved_SaveScore)
4830 FadeOut(REDRAW_FIELD);
4833 game_status = GAME_MODE_MAIN;
4835 DrawAndFadeInMainMenu(REDRAW_FIELD);
4840 if (level_nr == leveldir_current->handicap_level)
4842 leveldir_current->handicap_level++;
4843 SaveLevelSetup_SeriesInfo();
4846 if (level_nr < leveldir_current->last_level)
4847 raise_level = TRUE; /* advance to next level */
4849 if ((hi_pos = NewHiScore()) >= 0)
4851 game_status = GAME_MODE_SCORES;
4853 DrawHallOfFame(hi_pos);
4864 FadeOut(REDRAW_FIELD);
4867 game_status = GAME_MODE_MAIN;
4875 DrawAndFadeInMainMenu(REDRAW_FIELD);
4884 LoadScore(level_nr);
4886 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4887 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4890 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4892 if (local_player->score_final > highscore[k].Score)
4894 /* player has made it to the hall of fame */
4896 if (k < MAX_SCORE_ENTRIES - 1)
4898 int m = MAX_SCORE_ENTRIES - 1;
4901 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4902 if (strEqual(setup.player_name, highscore[l].Name))
4904 if (m == k) /* player's new highscore overwrites his old one */
4908 for (l = m; l > k; l--)
4910 strcpy(highscore[l].Name, highscore[l - 1].Name);
4911 highscore[l].Score = highscore[l - 1].Score;
4918 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4919 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4920 highscore[k].Score = local_player->score_final;
4926 else if (!strncmp(setup.player_name, highscore[k].Name,
4927 MAX_PLAYER_NAME_LEN))
4928 break; /* player already there with a higher score */
4934 SaveScore(level_nr);
4939 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4941 int element = Feld[x][y];
4942 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4943 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4944 int horiz_move = (dx != 0);
4945 int sign = (horiz_move ? dx : dy);
4946 int step = sign * element_info[element].move_stepsize;
4948 /* special values for move stepsize for spring and things on conveyor belt */
4951 if (CAN_FALL(element) &&
4952 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4953 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4954 else if (element == EL_SPRING)
4955 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4961 inline static int getElementMoveStepsize(int x, int y)
4963 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4966 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4968 if (player->GfxAction != action || player->GfxDir != dir)
4971 printf("Player frame reset! (%d => %d, %d => %d)\n",
4972 player->GfxAction, action, player->GfxDir, dir);
4975 player->GfxAction = action;
4976 player->GfxDir = dir;
4978 player->StepFrame = 0;
4982 #if USE_GFX_RESET_GFX_ANIMATION
4983 static void ResetGfxFrame(int x, int y, boolean redraw)
4985 int element = Feld[x][y];
4986 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4987 int last_gfx_frame = GfxFrame[x][y];
4989 if (graphic_info[graphic].anim_global_sync)
4990 GfxFrame[x][y] = FrameCounter;
4991 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4992 GfxFrame[x][y] = CustomValue[x][y];
4993 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4994 GfxFrame[x][y] = element_info[element].collect_score;
4995 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4996 GfxFrame[x][y] = ChangeDelay[x][y];
4998 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4999 DrawLevelGraphicAnimation(x, y, graphic);
5003 static void ResetGfxAnimation(int x, int y)
5005 GfxAction[x][y] = ACTION_DEFAULT;
5006 GfxDir[x][y] = MovDir[x][y];
5009 #if USE_GFX_RESET_GFX_ANIMATION
5010 ResetGfxFrame(x, y, FALSE);
5014 static void ResetRandomAnimationValue(int x, int y)
5016 GfxRandom[x][y] = INIT_GFX_RANDOM();
5019 void InitMovingField(int x, int y, int direction)
5021 int element = Feld[x][y];
5022 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5023 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5026 boolean is_moving_before, is_moving_after;
5028 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5031 /* check if element was/is moving or being moved before/after mode change */
5034 is_moving_before = (WasJustMoving[x][y] != 0);
5036 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5037 is_moving_before = WasJustMoving[x][y];
5040 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5042 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5044 /* reset animation only for moving elements which change direction of moving
5045 or which just started or stopped moving
5046 (else CEs with property "can move" / "not moving" are reset each frame) */
5047 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5049 if (is_moving_before != is_moving_after ||
5050 direction != MovDir[x][y])
5051 ResetGfxAnimation(x, y);
5053 if ((is_moving_before || is_moving_after) && !continues_moving)
5054 ResetGfxAnimation(x, y);
5057 if (!continues_moving)
5058 ResetGfxAnimation(x, y);
5061 MovDir[x][y] = direction;
5062 GfxDir[x][y] = direction;
5064 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5065 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5066 direction == MV_DOWN && CAN_FALL(element) ?
5067 ACTION_FALLING : ACTION_MOVING);
5069 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5070 ACTION_FALLING : ACTION_MOVING);
5073 /* this is needed for CEs with property "can move" / "not moving" */
5075 if (is_moving_after)
5077 if (Feld[newx][newy] == EL_EMPTY)
5078 Feld[newx][newy] = EL_BLOCKED;
5080 MovDir[newx][newy] = MovDir[x][y];
5082 #if USE_NEW_CUSTOM_VALUE
5083 CustomValue[newx][newy] = CustomValue[x][y];
5086 GfxFrame[newx][newy] = GfxFrame[x][y];
5087 GfxRandom[newx][newy] = GfxRandom[x][y];
5088 GfxAction[newx][newy] = GfxAction[x][y];
5089 GfxDir[newx][newy] = GfxDir[x][y];
5093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5095 int direction = MovDir[x][y];
5096 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5097 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5103 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5105 int oldx = x, oldy = y;
5106 int direction = MovDir[x][y];
5108 if (direction == MV_LEFT)
5110 else if (direction == MV_RIGHT)
5112 else if (direction == MV_UP)
5114 else if (direction == MV_DOWN)
5117 *comes_from_x = oldx;
5118 *comes_from_y = oldy;
5121 int MovingOrBlocked2Element(int x, int y)
5123 int element = Feld[x][y];
5125 if (element == EL_BLOCKED)
5129 Blocked2Moving(x, y, &oldx, &oldy);
5130 return Feld[oldx][oldy];
5136 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5138 /* like MovingOrBlocked2Element(), but if element is moving
5139 and (x,y) is the field the moving element is just leaving,
5140 return EL_BLOCKED instead of the element value */
5141 int element = Feld[x][y];
5143 if (IS_MOVING(x, y))
5145 if (element == EL_BLOCKED)
5149 Blocked2Moving(x, y, &oldx, &oldy);
5150 return Feld[oldx][oldy];
5159 static void RemoveField(int x, int y)
5161 Feld[x][y] = EL_EMPTY;
5167 #if USE_NEW_CUSTOM_VALUE
5168 CustomValue[x][y] = 0;
5172 ChangeDelay[x][y] = 0;
5173 ChangePage[x][y] = -1;
5174 Pushed[x][y] = FALSE;
5177 ExplodeField[x][y] = EX_TYPE_NONE;
5180 GfxElement[x][y] = EL_UNDEFINED;
5181 GfxAction[x][y] = ACTION_DEFAULT;
5182 GfxDir[x][y] = MV_NONE;
5184 /* !!! this would prevent the removed tile from being redrawn !!! */
5185 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5189 void RemoveMovingField(int x, int y)
5191 int oldx = x, oldy = y, newx = x, newy = y;
5192 int element = Feld[x][y];
5193 int next_element = EL_UNDEFINED;
5195 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5198 if (IS_MOVING(x, y))
5200 Moving2Blocked(x, y, &newx, &newy);
5202 if (Feld[newx][newy] != EL_BLOCKED)
5204 /* element is moving, but target field is not free (blocked), but
5205 already occupied by something different (example: acid pool);
5206 in this case, only remove the moving field, but not the target */
5208 RemoveField(oldx, oldy);
5210 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5212 TEST_DrawLevelField(oldx, oldy);
5217 else if (element == EL_BLOCKED)
5219 Blocked2Moving(x, y, &oldx, &oldy);
5220 if (!IS_MOVING(oldx, oldy))
5224 if (element == EL_BLOCKED &&
5225 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5226 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5227 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5228 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5229 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5230 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5231 next_element = get_next_element(Feld[oldx][oldy]);
5233 RemoveField(oldx, oldy);
5234 RemoveField(newx, newy);
5236 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5238 if (next_element != EL_UNDEFINED)
5239 Feld[oldx][oldy] = next_element;
5241 TEST_DrawLevelField(oldx, oldy);
5242 TEST_DrawLevelField(newx, newy);
5245 void DrawDynamite(int x, int y)
5247 int sx = SCREENX(x), sy = SCREENY(y);
5248 int graphic = el2img(Feld[x][y]);
5251 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5254 if (IS_WALKABLE_INSIDE(Back[x][y]))
5258 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5259 else if (Store[x][y])
5260 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5262 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5264 if (Back[x][y] || Store[x][y])
5265 DrawGraphicThruMask(sx, sy, graphic, frame);
5267 DrawGraphic(sx, sy, graphic, frame);
5270 void CheckDynamite(int x, int y)
5272 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5276 if (MovDelay[x][y] != 0)
5279 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5285 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5290 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5292 boolean num_checked_players = 0;
5295 for (i = 0; i < MAX_PLAYERS; i++)
5297 if (stored_player[i].active)
5299 int sx = stored_player[i].jx;
5300 int sy = stored_player[i].jy;
5302 if (num_checked_players == 0)
5309 *sx1 = MIN(*sx1, sx);
5310 *sy1 = MIN(*sy1, sy);
5311 *sx2 = MAX(*sx2, sx);
5312 *sy2 = MAX(*sy2, sy);
5315 num_checked_players++;
5320 static boolean checkIfAllPlayersFitToScreen_RND()
5322 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5324 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5326 return (sx2 - sx1 < SCR_FIELDX &&
5327 sy2 - sy1 < SCR_FIELDY);
5330 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5332 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5334 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5336 *sx = (sx1 + sx2) / 2;
5337 *sy = (sy1 + sy2) / 2;
5340 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5341 boolean center_screen, boolean quick_relocation)
5343 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5344 boolean no_delay = (tape.warp_forward);
5345 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5346 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5348 if (quick_relocation)
5350 int offset = game.scroll_delay_value;
5352 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5354 if (!level.shifted_relocation || center_screen)
5356 /* quick relocation (without scrolling), with centering of screen */
5358 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5359 x > SBX_Right + MIDPOSX ? SBX_Right :
5362 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5363 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5368 /* quick relocation (without scrolling), but do not center screen */
5370 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5371 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5374 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5375 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5378 int offset_x = x + (scroll_x - center_scroll_x);
5379 int offset_y = y + (scroll_y - center_scroll_y);
5381 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5382 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5383 offset_x - MIDPOSX);
5385 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5386 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5387 offset_y - MIDPOSY);
5392 /* quick relocation (without scrolling), inside visible screen area */
5394 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5395 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5396 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5398 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5399 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5400 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5402 /* don't scroll over playfield boundaries */
5403 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5404 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5406 /* don't scroll over playfield boundaries */
5407 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5408 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5411 RedrawPlayfield(TRUE, 0,0,0,0);
5416 int scroll_xx, scroll_yy;
5418 if (!level.shifted_relocation || center_screen)
5420 /* visible relocation (with scrolling), with centering of screen */
5422 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5423 x > SBX_Right + MIDPOSX ? SBX_Right :
5426 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5427 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5432 /* visible relocation (with scrolling), but do not center screen */
5434 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5435 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5438 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5439 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5442 int offset_x = x + (scroll_x - center_scroll_x);
5443 int offset_y = y + (scroll_y - center_scroll_y);
5445 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5446 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5447 offset_x - MIDPOSX);
5449 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5450 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5451 offset_y - MIDPOSY);
5456 /* visible relocation (with scrolling), with centering of screen */
5458 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5459 x > SBX_Right + MIDPOSX ? SBX_Right :
5462 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5463 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5467 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5469 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5472 int fx = FX, fy = FY;
5474 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5475 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5477 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5483 fx += dx * TILEX / 2;
5484 fy += dy * TILEY / 2;
5486 ScrollLevel(dx, dy);
5489 /* scroll in two steps of half tile size to make things smoother */
5490 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5492 Delay(wait_delay_value);
5494 /* scroll second step to align at full tile size */
5496 Delay(wait_delay_value);
5501 Delay(wait_delay_value);
5505 void RelocatePlayer(int jx, int jy, int el_player_raw)
5507 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5508 int player_nr = GET_PLAYER_NR(el_player);
5509 struct PlayerInfo *player = &stored_player[player_nr];
5510 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5511 boolean no_delay = (tape.warp_forward);
5512 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5513 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5514 int old_jx = player->jx;
5515 int old_jy = player->jy;
5516 int old_element = Feld[old_jx][old_jy];
5517 int element = Feld[jx][jy];
5518 boolean player_relocated = (old_jx != jx || old_jy != jy);
5520 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5521 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5522 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5523 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5524 int leave_side_horiz = move_dir_horiz;
5525 int leave_side_vert = move_dir_vert;
5526 int enter_side = enter_side_horiz | enter_side_vert;
5527 int leave_side = leave_side_horiz | leave_side_vert;
5529 if (player->GameOver) /* do not reanimate dead player */
5532 if (!player_relocated) /* no need to relocate the player */
5535 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5537 RemoveField(jx, jy); /* temporarily remove newly placed player */
5538 DrawLevelField(jx, jy);
5541 if (player->present)
5543 while (player->MovPos)
5545 ScrollPlayer(player, SCROLL_GO_ON);
5546 ScrollScreen(NULL, SCROLL_GO_ON);
5548 AdvanceFrameAndPlayerCounters(player->index_nr);
5553 Delay(wait_delay_value);
5556 DrawPlayer(player); /* needed here only to cleanup last field */
5557 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5559 player->is_moving = FALSE;
5562 if (IS_CUSTOM_ELEMENT(old_element))
5563 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5565 player->index_bit, leave_side);
5567 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5569 player->index_bit, leave_side);
5571 Feld[jx][jy] = el_player;
5572 InitPlayerField(jx, jy, el_player, TRUE);
5574 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5576 Feld[jx][jy] = element;
5577 InitField(jx, jy, FALSE);
5580 /* only visually relocate centered player */
5581 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5582 FALSE, level.instant_relocation);
5584 TestIfPlayerTouchesBadThing(jx, jy);
5585 TestIfPlayerTouchesCustomElement(jx, jy);
5587 if (IS_CUSTOM_ELEMENT(element))
5588 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5589 player->index_bit, enter_side);
5591 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5592 player->index_bit, enter_side);
5595 void Explode(int ex, int ey, int phase, int mode)
5601 /* !!! eliminate this variable !!! */
5602 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5604 if (game.explosions_delayed)
5606 ExplodeField[ex][ey] = mode;
5610 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5612 int center_element = Feld[ex][ey];
5613 int artwork_element, explosion_element; /* set these values later */
5616 /* --- This is only really needed (and now handled) in "Impact()". --- */
5617 /* do not explode moving elements that left the explode field in time */
5618 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5619 center_element == EL_EMPTY &&
5620 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5625 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5626 if (mode == EX_TYPE_NORMAL ||
5627 mode == EX_TYPE_CENTER ||
5628 mode == EX_TYPE_CROSS)
5629 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5632 /* remove things displayed in background while burning dynamite */
5633 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5636 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5638 /* put moving element to center field (and let it explode there) */
5639 center_element = MovingOrBlocked2Element(ex, ey);
5640 RemoveMovingField(ex, ey);
5641 Feld[ex][ey] = center_element;
5644 /* now "center_element" is finally determined -- set related values now */
5645 artwork_element = center_element; /* for custom player artwork */
5646 explosion_element = center_element; /* for custom player artwork */
5648 if (IS_PLAYER(ex, ey))
5650 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5652 artwork_element = stored_player[player_nr].artwork_element;
5654 if (level.use_explosion_element[player_nr])
5656 explosion_element = level.explosion_element[player_nr];
5657 artwork_element = explosion_element;
5662 if (mode == EX_TYPE_NORMAL ||
5663 mode == EX_TYPE_CENTER ||
5664 mode == EX_TYPE_CROSS)
5665 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5668 last_phase = element_info[explosion_element].explosion_delay + 1;
5670 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5672 int xx = x - ex + 1;
5673 int yy = y - ey + 1;
5676 if (!IN_LEV_FIELD(x, y) ||
5677 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5678 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5681 element = Feld[x][y];
5683 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5685 element = MovingOrBlocked2Element(x, y);
5687 if (!IS_EXPLOSION_PROOF(element))
5688 RemoveMovingField(x, y);
5691 /* indestructible elements can only explode in center (but not flames) */
5692 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5693 mode == EX_TYPE_BORDER)) ||
5694 element == EL_FLAMES)
5697 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5698 behaviour, for example when touching a yamyam that explodes to rocks
5699 with active deadly shield, a rock is created under the player !!! */
5700 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5702 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5703 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5704 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5706 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5709 if (IS_ACTIVE_BOMB(element))
5711 /* re-activate things under the bomb like gate or penguin */
5712 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5719 /* save walkable background elements while explosion on same tile */
5720 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5721 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5722 Back[x][y] = element;
5724 /* ignite explodable elements reached by other explosion */
5725 if (element == EL_EXPLOSION)
5726 element = Store2[x][y];
5728 if (AmoebaNr[x][y] &&
5729 (element == EL_AMOEBA_FULL ||
5730 element == EL_BD_AMOEBA ||
5731 element == EL_AMOEBA_GROWING))
5733 AmoebaCnt[AmoebaNr[x][y]]--;
5734 AmoebaCnt2[AmoebaNr[x][y]]--;
5739 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5741 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5743 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5745 if (PLAYERINFO(ex, ey)->use_murphy)
5746 Store[x][y] = EL_EMPTY;
5749 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5750 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5751 else if (ELEM_IS_PLAYER(center_element))
5752 Store[x][y] = EL_EMPTY;
5753 else if (center_element == EL_YAMYAM)
5754 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5755 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5756 Store[x][y] = element_info[center_element].content.e[xx][yy];
5758 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5759 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5760 otherwise) -- FIX THIS !!! */
5761 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5762 Store[x][y] = element_info[element].content.e[1][1];
5764 else if (!CAN_EXPLODE(element))
5765 Store[x][y] = element_info[element].content.e[1][1];
5768 Store[x][y] = EL_EMPTY;
5770 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5771 center_element == EL_AMOEBA_TO_DIAMOND)
5772 Store2[x][y] = element;
5774 Feld[x][y] = EL_EXPLOSION;
5775 GfxElement[x][y] = artwork_element;
5777 ExplodePhase[x][y] = 1;
5778 ExplodeDelay[x][y] = last_phase;
5783 if (center_element == EL_YAMYAM)
5784 game.yamyam_content_nr =
5785 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5797 GfxFrame[x][y] = 0; /* restart explosion animation */
5799 last_phase = ExplodeDelay[x][y];
5801 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5805 /* activate this even in non-DEBUG version until cause for crash in
5806 getGraphicAnimationFrame() (see below) is found and eliminated */
5812 /* this can happen if the player leaves an explosion just in time */
5813 if (GfxElement[x][y] == EL_UNDEFINED)
5814 GfxElement[x][y] = EL_EMPTY;
5816 if (GfxElement[x][y] == EL_UNDEFINED)
5819 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5820 printf("Explode(): This should never happen!\n");
5823 GfxElement[x][y] = EL_EMPTY;
5829 border_element = Store2[x][y];
5830 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5831 border_element = StorePlayer[x][y];
5833 if (phase == element_info[border_element].ignition_delay ||
5834 phase == last_phase)
5836 boolean border_explosion = FALSE;
5838 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5839 !PLAYER_EXPLOSION_PROTECTED(x, y))
5841 KillPlayerUnlessExplosionProtected(x, y);
5842 border_explosion = TRUE;
5844 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5846 Feld[x][y] = Store2[x][y];
5849 border_explosion = TRUE;
5851 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5853 AmoebeUmwandeln(x, y);
5855 border_explosion = TRUE;
5858 /* if an element just explodes due to another explosion (chain-reaction),
5859 do not immediately end the new explosion when it was the last frame of
5860 the explosion (as it would be done in the following "if"-statement!) */
5861 if (border_explosion && phase == last_phase)
5865 if (phase == last_phase)
5869 element = Feld[x][y] = Store[x][y];
5870 Store[x][y] = Store2[x][y] = 0;
5871 GfxElement[x][y] = EL_UNDEFINED;
5873 /* player can escape from explosions and might therefore be still alive */
5874 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5875 element <= EL_PLAYER_IS_EXPLODING_4)
5877 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5878 int explosion_element = EL_PLAYER_1 + player_nr;
5879 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5880 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5882 if (level.use_explosion_element[player_nr])
5883 explosion_element = level.explosion_element[player_nr];
5885 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5886 element_info[explosion_element].content.e[xx][yy]);
5889 /* restore probably existing indestructible background element */
5890 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5891 element = Feld[x][y] = Back[x][y];
5894 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5895 GfxDir[x][y] = MV_NONE;
5896 ChangeDelay[x][y] = 0;
5897 ChangePage[x][y] = -1;
5899 #if USE_NEW_CUSTOM_VALUE
5900 CustomValue[x][y] = 0;
5903 InitField_WithBug2(x, y, FALSE);
5905 TEST_DrawLevelField(x, y);
5907 TestIfElementTouchesCustomElement(x, y);
5909 if (GFX_CRUMBLED(element))
5910 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5912 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5913 StorePlayer[x][y] = 0;
5915 if (ELEM_IS_PLAYER(element))
5916 RelocatePlayer(x, y, element);
5918 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5920 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5921 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5924 TEST_DrawLevelFieldCrumbledSand(x, y);
5926 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5928 DrawLevelElement(x, y, Back[x][y]);
5929 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5931 else if (IS_WALKABLE_UNDER(Back[x][y]))
5933 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934 DrawLevelElementThruMask(x, y, Back[x][y]);
5936 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5937 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5941 void DynaExplode(int ex, int ey)
5944 int dynabomb_element = Feld[ex][ey];
5945 int dynabomb_size = 1;
5946 boolean dynabomb_xl = FALSE;
5947 struct PlayerInfo *player;
5948 static int xy[4][2] =
5956 if (IS_ACTIVE_BOMB(dynabomb_element))
5958 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5959 dynabomb_size = player->dynabomb_size;
5960 dynabomb_xl = player->dynabomb_xl;
5961 player->dynabombs_left++;
5964 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5966 for (i = 0; i < NUM_DIRECTIONS; i++)
5968 for (j = 1; j <= dynabomb_size; j++)
5970 int x = ex + j * xy[i][0];
5971 int y = ey + j * xy[i][1];
5974 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5977 element = Feld[x][y];
5979 /* do not restart explosions of fields with active bombs */
5980 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5983 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5985 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5986 !IS_DIGGABLE(element) && !dynabomb_xl)
5992 void Bang(int x, int y)
5994 int element = MovingOrBlocked2Element(x, y);
5995 int explosion_type = EX_TYPE_NORMAL;
5997 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5999 struct PlayerInfo *player = PLAYERINFO(x, y);
6001 #if USE_FIX_CE_ACTION_WITH_PLAYER
6002 element = Feld[x][y] = player->initial_element;
6004 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6005 player->element_nr);
6008 if (level.use_explosion_element[player->index_nr])
6010 int explosion_element = level.explosion_element[player->index_nr];
6012 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6013 explosion_type = EX_TYPE_CROSS;
6014 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6015 explosion_type = EX_TYPE_CENTER;
6023 case EL_BD_BUTTERFLY:
6026 case EL_DARK_YAMYAM:
6030 RaiseScoreElement(element);
6033 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6034 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6035 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6036 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6037 case EL_DYNABOMB_INCREASE_NUMBER:
6038 case EL_DYNABOMB_INCREASE_SIZE:
6039 case EL_DYNABOMB_INCREASE_POWER:
6040 explosion_type = EX_TYPE_DYNA;
6043 case EL_DC_LANDMINE:
6045 case EL_EM_EXIT_OPEN:
6046 case EL_EM_STEEL_EXIT_OPEN:
6048 explosion_type = EX_TYPE_CENTER;
6053 case EL_LAMP_ACTIVE:
6054 case EL_AMOEBA_TO_DIAMOND:
6055 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6056 explosion_type = EX_TYPE_CENTER;
6060 if (element_info[element].explosion_type == EXPLODES_CROSS)
6061 explosion_type = EX_TYPE_CROSS;
6062 else if (element_info[element].explosion_type == EXPLODES_1X1)
6063 explosion_type = EX_TYPE_CENTER;
6067 if (explosion_type == EX_TYPE_DYNA)
6070 Explode(x, y, EX_PHASE_START, explosion_type);
6072 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6075 void SplashAcid(int x, int y)
6077 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6078 (!IN_LEV_FIELD(x - 1, y - 2) ||
6079 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6080 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6082 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6083 (!IN_LEV_FIELD(x + 1, y - 2) ||
6084 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6085 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6087 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6090 static void InitBeltMovement()
6092 static int belt_base_element[4] =
6094 EL_CONVEYOR_BELT_1_LEFT,
6095 EL_CONVEYOR_BELT_2_LEFT,
6096 EL_CONVEYOR_BELT_3_LEFT,
6097 EL_CONVEYOR_BELT_4_LEFT
6099 static int belt_base_active_element[4] =
6101 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6102 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6103 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6104 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6109 /* set frame order for belt animation graphic according to belt direction */
6110 for (i = 0; i < NUM_BELTS; i++)
6114 for (j = 0; j < NUM_BELT_PARTS; j++)
6116 int element = belt_base_active_element[belt_nr] + j;
6117 int graphic_1 = el2img(element);
6118 int graphic_2 = el2panelimg(element);
6120 if (game.belt_dir[i] == MV_LEFT)
6122 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6123 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6127 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6128 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6133 SCAN_PLAYFIELD(x, y)
6135 int element = Feld[x][y];
6137 for (i = 0; i < NUM_BELTS; i++)
6139 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6141 int e_belt_nr = getBeltNrFromBeltElement(element);
6144 if (e_belt_nr == belt_nr)
6146 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6148 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6155 static void ToggleBeltSwitch(int x, int y)
6157 static int belt_base_element[4] =
6159 EL_CONVEYOR_BELT_1_LEFT,
6160 EL_CONVEYOR_BELT_2_LEFT,
6161 EL_CONVEYOR_BELT_3_LEFT,
6162 EL_CONVEYOR_BELT_4_LEFT
6164 static int belt_base_active_element[4] =
6166 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6167 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6168 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6169 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6171 static int belt_base_switch_element[4] =
6173 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6174 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6175 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6176 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6178 static int belt_move_dir[4] =
6186 int element = Feld[x][y];
6187 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6188 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6189 int belt_dir = belt_move_dir[belt_dir_nr];
6192 if (!IS_BELT_SWITCH(element))
6195 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6196 game.belt_dir[belt_nr] = belt_dir;
6198 if (belt_dir_nr == 3)
6201 /* set frame order for belt animation graphic according to belt direction */
6202 for (i = 0; i < NUM_BELT_PARTS; i++)
6204 int element = belt_base_active_element[belt_nr] + i;
6205 int graphic_1 = el2img(element);
6206 int graphic_2 = el2panelimg(element);
6208 if (belt_dir == MV_LEFT)
6210 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6211 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6215 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6216 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6220 SCAN_PLAYFIELD(xx, yy)
6222 int element = Feld[xx][yy];
6224 if (IS_BELT_SWITCH(element))
6226 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6228 if (e_belt_nr == belt_nr)
6230 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6231 TEST_DrawLevelField(xx, yy);
6234 else if (IS_BELT(element) && belt_dir != MV_NONE)
6236 int e_belt_nr = getBeltNrFromBeltElement(element);
6238 if (e_belt_nr == belt_nr)
6240 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6242 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6243 TEST_DrawLevelField(xx, yy);
6246 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6248 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6250 if (e_belt_nr == belt_nr)
6252 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6254 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6255 TEST_DrawLevelField(xx, yy);
6261 static void ToggleSwitchgateSwitch(int x, int y)
6265 game.switchgate_pos = !game.switchgate_pos;
6267 SCAN_PLAYFIELD(xx, yy)
6269 int element = Feld[xx][yy];
6271 #if !USE_BOTH_SWITCHGATE_SWITCHES
6272 if (element == EL_SWITCHGATE_SWITCH_UP ||
6273 element == EL_SWITCHGATE_SWITCH_DOWN)
6275 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6276 TEST_DrawLevelField(xx, yy);
6278 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6279 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6281 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6282 TEST_DrawLevelField(xx, yy);
6285 if (element == EL_SWITCHGATE_SWITCH_UP)
6287 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6288 TEST_DrawLevelField(xx, yy);
6290 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6292 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6293 TEST_DrawLevelField(xx, yy);
6295 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6297 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6298 TEST_DrawLevelField(xx, yy);
6300 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6302 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6303 TEST_DrawLevelField(xx, yy);
6306 else if (element == EL_SWITCHGATE_OPEN ||
6307 element == EL_SWITCHGATE_OPENING)
6309 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6311 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6313 else if (element == EL_SWITCHGATE_CLOSED ||
6314 element == EL_SWITCHGATE_CLOSING)
6316 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6318 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6323 static int getInvisibleActiveFromInvisibleElement(int element)
6325 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6326 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6327 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6331 static int getInvisibleFromInvisibleActiveElement(int element)
6333 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6334 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6335 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6339 static void RedrawAllLightSwitchesAndInvisibleElements()
6343 SCAN_PLAYFIELD(x, y)
6345 int element = Feld[x][y];
6347 if (element == EL_LIGHT_SWITCH &&
6348 game.light_time_left > 0)
6350 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6351 TEST_DrawLevelField(x, y);
6353 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6354 game.light_time_left == 0)
6356 Feld[x][y] = EL_LIGHT_SWITCH;
6357 TEST_DrawLevelField(x, y);
6359 else if (element == EL_EMC_DRIPPER &&
6360 game.light_time_left > 0)
6362 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6363 TEST_DrawLevelField(x, y);
6365 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6366 game.light_time_left == 0)
6368 Feld[x][y] = EL_EMC_DRIPPER;
6369 TEST_DrawLevelField(x, y);
6371 else if (element == EL_INVISIBLE_STEELWALL ||
6372 element == EL_INVISIBLE_WALL ||
6373 element == EL_INVISIBLE_SAND)
6375 if (game.light_time_left > 0)
6376 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6378 TEST_DrawLevelField(x, y);
6380 /* uncrumble neighbour fields, if needed */
6381 if (element == EL_INVISIBLE_SAND)
6382 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6384 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6385 element == EL_INVISIBLE_WALL_ACTIVE ||
6386 element == EL_INVISIBLE_SAND_ACTIVE)
6388 if (game.light_time_left == 0)
6389 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6391 TEST_DrawLevelField(x, y);
6393 /* re-crumble neighbour fields, if needed */
6394 if (element == EL_INVISIBLE_SAND)
6395 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6400 static void RedrawAllInvisibleElementsForLenses()
6404 SCAN_PLAYFIELD(x, y)
6406 int element = Feld[x][y];
6408 if (element == EL_EMC_DRIPPER &&
6409 game.lenses_time_left > 0)
6411 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6412 TEST_DrawLevelField(x, y);
6414 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6415 game.lenses_time_left == 0)
6417 Feld[x][y] = EL_EMC_DRIPPER;
6418 TEST_DrawLevelField(x, y);
6420 else if (element == EL_INVISIBLE_STEELWALL ||
6421 element == EL_INVISIBLE_WALL ||
6422 element == EL_INVISIBLE_SAND)
6424 if (game.lenses_time_left > 0)
6425 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6427 TEST_DrawLevelField(x, y);
6429 /* uncrumble neighbour fields, if needed */
6430 if (element == EL_INVISIBLE_SAND)
6431 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6433 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6434 element == EL_INVISIBLE_WALL_ACTIVE ||
6435 element == EL_INVISIBLE_SAND_ACTIVE)
6437 if (game.lenses_time_left == 0)
6438 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6440 TEST_DrawLevelField(x, y);
6442 /* re-crumble neighbour fields, if needed */
6443 if (element == EL_INVISIBLE_SAND)
6444 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6449 static void RedrawAllInvisibleElementsForMagnifier()
6453 SCAN_PLAYFIELD(x, y)
6455 int element = Feld[x][y];
6457 if (element == EL_EMC_FAKE_GRASS &&
6458 game.magnify_time_left > 0)
6460 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6461 TEST_DrawLevelField(x, y);
6463 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6464 game.magnify_time_left == 0)
6466 Feld[x][y] = EL_EMC_FAKE_GRASS;
6467 TEST_DrawLevelField(x, y);
6469 else if (IS_GATE_GRAY(element) &&
6470 game.magnify_time_left > 0)
6472 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6473 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6474 IS_EM_GATE_GRAY(element) ?
6475 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6476 IS_EMC_GATE_GRAY(element) ?
6477 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6478 IS_DC_GATE_GRAY(element) ?
6479 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6481 TEST_DrawLevelField(x, y);
6483 else if (IS_GATE_GRAY_ACTIVE(element) &&
6484 game.magnify_time_left == 0)
6486 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6487 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6488 IS_EM_GATE_GRAY_ACTIVE(element) ?
6489 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6490 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6491 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6492 IS_DC_GATE_GRAY_ACTIVE(element) ?
6493 EL_DC_GATE_WHITE_GRAY :
6495 TEST_DrawLevelField(x, y);
6500 static void ToggleLightSwitch(int x, int y)
6502 int element = Feld[x][y];
6504 game.light_time_left =
6505 (element == EL_LIGHT_SWITCH ?
6506 level.time_light * FRAMES_PER_SECOND : 0);
6508 RedrawAllLightSwitchesAndInvisibleElements();
6511 static void ActivateTimegateSwitch(int x, int y)
6515 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6517 SCAN_PLAYFIELD(xx, yy)
6519 int element = Feld[xx][yy];
6521 if (element == EL_TIMEGATE_CLOSED ||
6522 element == EL_TIMEGATE_CLOSING)
6524 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6525 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6529 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6531 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6532 TEST_DrawLevelField(xx, yy);
6539 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6540 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6542 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6546 void Impact(int x, int y)
6548 boolean last_line = (y == lev_fieldy - 1);
6549 boolean object_hit = FALSE;
6550 boolean impact = (last_line || object_hit);
6551 int element = Feld[x][y];
6552 int smashed = EL_STEELWALL;
6554 if (!last_line) /* check if element below was hit */
6556 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6559 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6560 MovDir[x][y + 1] != MV_DOWN ||
6561 MovPos[x][y + 1] <= TILEY / 2));
6563 /* do not smash moving elements that left the smashed field in time */
6564 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6565 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6568 #if USE_QUICKSAND_IMPACT_BUGFIX
6569 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6571 RemoveMovingField(x, y + 1);
6572 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6573 Feld[x][y + 2] = EL_ROCK;
6574 TEST_DrawLevelField(x, y + 2);
6579 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6581 RemoveMovingField(x, y + 1);
6582 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6583 Feld[x][y + 2] = EL_ROCK;
6584 TEST_DrawLevelField(x, y + 2);
6591 smashed = MovingOrBlocked2Element(x, y + 1);
6593 impact = (last_line || object_hit);
6596 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6598 SplashAcid(x, y + 1);
6602 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6603 /* only reset graphic animation if graphic really changes after impact */
6605 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6607 ResetGfxAnimation(x, y);
6608 TEST_DrawLevelField(x, y);
6611 if (impact && CAN_EXPLODE_IMPACT(element))
6616 else if (impact && element == EL_PEARL &&
6617 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6619 ResetGfxAnimation(x, y);
6621 Feld[x][y] = EL_PEARL_BREAKING;
6622 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6625 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6627 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6632 if (impact && element == EL_AMOEBA_DROP)
6634 if (object_hit && IS_PLAYER(x, y + 1))
6635 KillPlayerUnlessEnemyProtected(x, y + 1);
6636 else if (object_hit && smashed == EL_PENGUIN)
6640 Feld[x][y] = EL_AMOEBA_GROWING;
6641 Store[x][y] = EL_AMOEBA_WET;
6643 ResetRandomAnimationValue(x, y);
6648 if (object_hit) /* check which object was hit */
6650 if ((CAN_PASS_MAGIC_WALL(element) &&
6651 (smashed == EL_MAGIC_WALL ||
6652 smashed == EL_BD_MAGIC_WALL)) ||
6653 (CAN_PASS_DC_MAGIC_WALL(element) &&
6654 smashed == EL_DC_MAGIC_WALL))
6657 int activated_magic_wall =
6658 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6659 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6660 EL_DC_MAGIC_WALL_ACTIVE);
6662 /* activate magic wall / mill */
6663 SCAN_PLAYFIELD(xx, yy)
6665 if (Feld[xx][yy] == smashed)
6666 Feld[xx][yy] = activated_magic_wall;
6669 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6670 game.magic_wall_active = TRUE;
6672 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6673 SND_MAGIC_WALL_ACTIVATING :
6674 smashed == EL_BD_MAGIC_WALL ?
6675 SND_BD_MAGIC_WALL_ACTIVATING :
6676 SND_DC_MAGIC_WALL_ACTIVATING));
6679 if (IS_PLAYER(x, y + 1))
6681 if (CAN_SMASH_PLAYER(element))
6683 KillPlayerUnlessEnemyProtected(x, y + 1);
6687 else if (smashed == EL_PENGUIN)
6689 if (CAN_SMASH_PLAYER(element))
6695 else if (element == EL_BD_DIAMOND)
6697 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6703 else if (((element == EL_SP_INFOTRON ||
6704 element == EL_SP_ZONK) &&
6705 (smashed == EL_SP_SNIKSNAK ||
6706 smashed == EL_SP_ELECTRON ||
6707 smashed == EL_SP_DISK_ORANGE)) ||
6708 (element == EL_SP_INFOTRON &&
6709 smashed == EL_SP_DISK_YELLOW))
6714 else if (CAN_SMASH_EVERYTHING(element))
6716 if (IS_CLASSIC_ENEMY(smashed) ||
6717 CAN_EXPLODE_SMASHED(smashed))
6722 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6724 if (smashed == EL_LAMP ||
6725 smashed == EL_LAMP_ACTIVE)
6730 else if (smashed == EL_NUT)
6732 Feld[x][y + 1] = EL_NUT_BREAKING;
6733 PlayLevelSound(x, y, SND_NUT_BREAKING);
6734 RaiseScoreElement(EL_NUT);
6737 else if (smashed == EL_PEARL)
6739 ResetGfxAnimation(x, y);
6741 Feld[x][y + 1] = EL_PEARL_BREAKING;
6742 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6745 else if (smashed == EL_DIAMOND)
6747 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6748 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6751 else if (IS_BELT_SWITCH(smashed))
6753 ToggleBeltSwitch(x, y + 1);
6755 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6756 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6757 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6758 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6760 ToggleSwitchgateSwitch(x, y + 1);
6762 else if (smashed == EL_LIGHT_SWITCH ||
6763 smashed == EL_LIGHT_SWITCH_ACTIVE)
6765 ToggleLightSwitch(x, y + 1);
6770 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6773 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6775 CheckElementChangeBySide(x, y + 1, smashed, element,
6776 CE_SWITCHED, CH_SIDE_TOP);
6777 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6783 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6788 /* play sound of magic wall / mill */
6790 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6791 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6792 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6794 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6795 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6796 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6797 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6798 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6799 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6804 /* play sound of object that hits the ground */
6805 if (last_line || object_hit)
6806 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6809 inline static void TurnRoundExt(int x, int y)
6821 { 0, 0 }, { 0, 0 }, { 0, 0 },
6826 int left, right, back;
6830 { MV_DOWN, MV_UP, MV_RIGHT },
6831 { MV_UP, MV_DOWN, MV_LEFT },
6833 { MV_LEFT, MV_RIGHT, MV_DOWN },
6837 { MV_RIGHT, MV_LEFT, MV_UP }
6840 int element = Feld[x][y];
6841 int move_pattern = element_info[element].move_pattern;
6843 int old_move_dir = MovDir[x][y];
6844 int left_dir = turn[old_move_dir].left;
6845 int right_dir = turn[old_move_dir].right;
6846 int back_dir = turn[old_move_dir].back;
6848 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6849 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6850 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6851 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6853 int left_x = x + left_dx, left_y = y + left_dy;
6854 int right_x = x + right_dx, right_y = y + right_dy;
6855 int move_x = x + move_dx, move_y = y + move_dy;
6859 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6861 TestIfBadThingTouchesOtherBadThing(x, y);
6863 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6864 MovDir[x][y] = right_dir;
6865 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6866 MovDir[x][y] = left_dir;
6868 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6870 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6873 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6875 TestIfBadThingTouchesOtherBadThing(x, y);
6877 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6878 MovDir[x][y] = left_dir;
6879 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6880 MovDir[x][y] = right_dir;
6882 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6884 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6887 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6889 TestIfBadThingTouchesOtherBadThing(x, y);
6891 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6892 MovDir[x][y] = left_dir;
6893 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6894 MovDir[x][y] = right_dir;
6896 if (MovDir[x][y] != old_move_dir)
6899 else if (element == EL_YAMYAM)
6901 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6902 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6904 if (can_turn_left && can_turn_right)
6905 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6906 else if (can_turn_left)
6907 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6908 else if (can_turn_right)
6909 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6911 MovDir[x][y] = back_dir;
6913 MovDelay[x][y] = 16 + 16 * RND(3);
6915 else if (element == EL_DARK_YAMYAM)
6917 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6919 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6922 if (can_turn_left && can_turn_right)
6923 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6924 else if (can_turn_left)
6925 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6926 else if (can_turn_right)
6927 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6929 MovDir[x][y] = back_dir;
6931 MovDelay[x][y] = 16 + 16 * RND(3);
6933 else if (element == EL_PACMAN)
6935 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6936 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6938 if (can_turn_left && can_turn_right)
6939 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6940 else if (can_turn_left)
6941 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6942 else if (can_turn_right)
6943 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6945 MovDir[x][y] = back_dir;
6947 MovDelay[x][y] = 6 + RND(40);
6949 else if (element == EL_PIG)
6951 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6952 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6953 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6954 boolean should_turn_left, should_turn_right, should_move_on;
6956 int rnd = RND(rnd_value);
6958 should_turn_left = (can_turn_left &&
6960 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6961 y + back_dy + left_dy)));
6962 should_turn_right = (can_turn_right &&
6964 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6965 y + back_dy + right_dy)));
6966 should_move_on = (can_move_on &&
6969 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6970 y + move_dy + left_dy) ||
6971 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6972 y + move_dy + right_dy)));
6974 if (should_turn_left || should_turn_right || should_move_on)
6976 if (should_turn_left && should_turn_right && should_move_on)
6977 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6978 rnd < 2 * rnd_value / 3 ? right_dir :
6980 else if (should_turn_left && should_turn_right)
6981 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6982 else if (should_turn_left && should_move_on)
6983 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6984 else if (should_turn_right && should_move_on)
6985 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6986 else if (should_turn_left)
6987 MovDir[x][y] = left_dir;
6988 else if (should_turn_right)
6989 MovDir[x][y] = right_dir;
6990 else if (should_move_on)
6991 MovDir[x][y] = old_move_dir;
6993 else if (can_move_on && rnd > rnd_value / 8)
6994 MovDir[x][y] = old_move_dir;
6995 else if (can_turn_left && can_turn_right)
6996 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6997 else if (can_turn_left && rnd > rnd_value / 8)
6998 MovDir[x][y] = left_dir;
6999 else if (can_turn_right && rnd > rnd_value/8)
7000 MovDir[x][y] = right_dir;
7002 MovDir[x][y] = back_dir;
7004 xx = x + move_xy[MovDir[x][y]].dx;
7005 yy = y + move_xy[MovDir[x][y]].dy;
7007 if (!IN_LEV_FIELD(xx, yy) ||
7008 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7009 MovDir[x][y] = old_move_dir;
7013 else if (element == EL_DRAGON)
7015 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7016 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7017 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7019 int rnd = RND(rnd_value);
7021 if (can_move_on && rnd > rnd_value / 8)
7022 MovDir[x][y] = old_move_dir;
7023 else if (can_turn_left && can_turn_right)
7024 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7025 else if (can_turn_left && rnd > rnd_value / 8)
7026 MovDir[x][y] = left_dir;
7027 else if (can_turn_right && rnd > rnd_value / 8)
7028 MovDir[x][y] = right_dir;
7030 MovDir[x][y] = back_dir;
7032 xx = x + move_xy[MovDir[x][y]].dx;
7033 yy = y + move_xy[MovDir[x][y]].dy;
7035 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7036 MovDir[x][y] = old_move_dir;
7040 else if (element == EL_MOLE)
7042 boolean can_move_on =
7043 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7044 IS_AMOEBOID(Feld[move_x][move_y]) ||
7045 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7048 boolean can_turn_left =
7049 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7050 IS_AMOEBOID(Feld[left_x][left_y])));
7052 boolean can_turn_right =
7053 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7054 IS_AMOEBOID(Feld[right_x][right_y])));
7056 if (can_turn_left && can_turn_right)
7057 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7058 else if (can_turn_left)
7059 MovDir[x][y] = left_dir;
7061 MovDir[x][y] = right_dir;
7064 if (MovDir[x][y] != old_move_dir)
7067 else if (element == EL_BALLOON)
7069 MovDir[x][y] = game.wind_direction;
7072 else if (element == EL_SPRING)
7074 #if USE_NEW_SPRING_BUMPER
7075 if (MovDir[x][y] & MV_HORIZONTAL)
7077 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7078 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7080 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7081 ResetGfxAnimation(move_x, move_y);
7082 TEST_DrawLevelField(move_x, move_y);
7084 MovDir[x][y] = back_dir;
7086 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7087 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7088 MovDir[x][y] = MV_NONE;
7091 if (MovDir[x][y] & MV_HORIZONTAL &&
7092 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7093 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7094 MovDir[x][y] = MV_NONE;
7099 else if (element == EL_ROBOT ||
7100 element == EL_SATELLITE ||
7101 element == EL_PENGUIN ||
7102 element == EL_EMC_ANDROID)
7104 int attr_x = -1, attr_y = -1;
7115 for (i = 0; i < MAX_PLAYERS; i++)
7117 struct PlayerInfo *player = &stored_player[i];
7118 int jx = player->jx, jy = player->jy;
7120 if (!player->active)
7124 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7132 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7133 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7134 game.engine_version < VERSION_IDENT(3,1,0,0)))
7140 if (element == EL_PENGUIN)
7143 static int xy[4][2] =
7151 for (i = 0; i < NUM_DIRECTIONS; i++)
7153 int ex = x + xy[i][0];
7154 int ey = y + xy[i][1];
7156 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7157 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7158 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7159 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7168 MovDir[x][y] = MV_NONE;
7170 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7171 else if (attr_x > x)
7172 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7174 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7175 else if (attr_y > y)
7176 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7178 if (element == EL_ROBOT)
7182 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7183 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7184 Moving2Blocked(x, y, &newx, &newy);
7186 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7187 MovDelay[x][y] = 8 + 8 * !RND(3);
7189 MovDelay[x][y] = 16;
7191 else if (element == EL_PENGUIN)
7197 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7199 boolean first_horiz = RND(2);
7200 int new_move_dir = MovDir[x][y];
7203 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7204 Moving2Blocked(x, y, &newx, &newy);
7206 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7210 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7211 Moving2Blocked(x, y, &newx, &newy);
7213 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7216 MovDir[x][y] = old_move_dir;
7220 else if (element == EL_SATELLITE)
7226 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7228 boolean first_horiz = RND(2);
7229 int new_move_dir = MovDir[x][y];
7232 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7233 Moving2Blocked(x, y, &newx, &newy);
7235 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7239 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7240 Moving2Blocked(x, y, &newx, &newy);
7242 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7245 MovDir[x][y] = old_move_dir;
7249 else if (element == EL_EMC_ANDROID)
7251 static int check_pos[16] =
7253 -1, /* 0 => (invalid) */
7254 7, /* 1 => MV_LEFT */
7255 3, /* 2 => MV_RIGHT */
7256 -1, /* 3 => (invalid) */
7258 0, /* 5 => MV_LEFT | MV_UP */
7259 2, /* 6 => MV_RIGHT | MV_UP */
7260 -1, /* 7 => (invalid) */
7261 5, /* 8 => MV_DOWN */
7262 6, /* 9 => MV_LEFT | MV_DOWN */
7263 4, /* 10 => MV_RIGHT | MV_DOWN */
7264 -1, /* 11 => (invalid) */
7265 -1, /* 12 => (invalid) */
7266 -1, /* 13 => (invalid) */
7267 -1, /* 14 => (invalid) */
7268 -1, /* 15 => (invalid) */
7276 { -1, -1, MV_LEFT | MV_UP },
7278 { +1, -1, MV_RIGHT | MV_UP },
7279 { +1, 0, MV_RIGHT },
7280 { +1, +1, MV_RIGHT | MV_DOWN },
7282 { -1, +1, MV_LEFT | MV_DOWN },
7285 int start_pos, check_order;
7286 boolean can_clone = FALSE;
7289 /* check if there is any free field around current position */
7290 for (i = 0; i < 8; i++)
7292 int newx = x + check_xy[i].dx;
7293 int newy = y + check_xy[i].dy;
7295 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7303 if (can_clone) /* randomly find an element to clone */
7307 start_pos = check_pos[RND(8)];
7308 check_order = (RND(2) ? -1 : +1);
7310 for (i = 0; i < 8; i++)
7312 int pos_raw = start_pos + i * check_order;
7313 int pos = (pos_raw + 8) % 8;
7314 int newx = x + check_xy[pos].dx;
7315 int newy = y + check_xy[pos].dy;
7317 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7319 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7320 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7322 Store[x][y] = Feld[newx][newy];
7331 if (can_clone) /* randomly find a direction to move */
7335 start_pos = check_pos[RND(8)];
7336 check_order = (RND(2) ? -1 : +1);
7338 for (i = 0; i < 8; i++)
7340 int pos_raw = start_pos + i * check_order;
7341 int pos = (pos_raw + 8) % 8;
7342 int newx = x + check_xy[pos].dx;
7343 int newy = y + check_xy[pos].dy;
7344 int new_move_dir = check_xy[pos].dir;
7346 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7348 MovDir[x][y] = new_move_dir;
7349 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7358 if (can_clone) /* cloning and moving successful */
7361 /* cannot clone -- try to move towards player */
7363 start_pos = check_pos[MovDir[x][y] & 0x0f];
7364 check_order = (RND(2) ? -1 : +1);
7366 for (i = 0; i < 3; i++)
7368 /* first check start_pos, then previous/next or (next/previous) pos */
7369 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7370 int pos = (pos_raw + 8) % 8;
7371 int newx = x + check_xy[pos].dx;
7372 int newy = y + check_xy[pos].dy;
7373 int new_move_dir = check_xy[pos].dir;
7375 if (IS_PLAYER(newx, newy))
7378 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7380 MovDir[x][y] = new_move_dir;
7381 MovDelay[x][y] = level.android_move_time * 8 + 1;
7388 else if (move_pattern == MV_TURNING_LEFT ||
7389 move_pattern == MV_TURNING_RIGHT ||
7390 move_pattern == MV_TURNING_LEFT_RIGHT ||
7391 move_pattern == MV_TURNING_RIGHT_LEFT ||
7392 move_pattern == MV_TURNING_RANDOM ||
7393 move_pattern == MV_ALL_DIRECTIONS)
7395 boolean can_turn_left =
7396 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7397 boolean can_turn_right =
7398 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7400 if (element_info[element].move_stepsize == 0) /* "not moving" */
7403 if (move_pattern == MV_TURNING_LEFT)
7404 MovDir[x][y] = left_dir;
7405 else if (move_pattern == MV_TURNING_RIGHT)
7406 MovDir[x][y] = right_dir;
7407 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7408 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7409 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7410 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7411 else if (move_pattern == MV_TURNING_RANDOM)
7412 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7413 can_turn_right && !can_turn_left ? right_dir :
7414 RND(2) ? left_dir : right_dir);
7415 else if (can_turn_left && can_turn_right)
7416 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7417 else if (can_turn_left)
7418 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7419 else if (can_turn_right)
7420 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7422 MovDir[x][y] = back_dir;
7424 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7426 else if (move_pattern == MV_HORIZONTAL ||
7427 move_pattern == MV_VERTICAL)
7429 if (move_pattern & old_move_dir)
7430 MovDir[x][y] = back_dir;
7431 else if (move_pattern == MV_HORIZONTAL)
7432 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7433 else if (move_pattern == MV_VERTICAL)
7434 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7436 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7438 else if (move_pattern & MV_ANY_DIRECTION)
7440 MovDir[x][y] = move_pattern;
7441 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7443 else if (move_pattern & MV_WIND_DIRECTION)
7445 MovDir[x][y] = game.wind_direction;
7446 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7448 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7450 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7451 MovDir[x][y] = left_dir;
7452 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7453 MovDir[x][y] = right_dir;
7455 if (MovDir[x][y] != old_move_dir)
7456 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7458 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7460 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7461 MovDir[x][y] = right_dir;
7462 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7463 MovDir[x][y] = left_dir;
7465 if (MovDir[x][y] != old_move_dir)
7466 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7468 else if (move_pattern == MV_TOWARDS_PLAYER ||
7469 move_pattern == MV_AWAY_FROM_PLAYER)
7471 int attr_x = -1, attr_y = -1;
7473 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7484 for (i = 0; i < MAX_PLAYERS; i++)
7486 struct PlayerInfo *player = &stored_player[i];
7487 int jx = player->jx, jy = player->jy;
7489 if (!player->active)
7493 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7501 MovDir[x][y] = MV_NONE;
7503 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7504 else if (attr_x > x)
7505 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7507 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7508 else if (attr_y > y)
7509 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7511 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7513 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7515 boolean first_horiz = RND(2);
7516 int new_move_dir = MovDir[x][y];
7518 if (element_info[element].move_stepsize == 0) /* "not moving" */
7520 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7521 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7527 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7528 Moving2Blocked(x, y, &newx, &newy);
7530 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7534 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7535 Moving2Blocked(x, y, &newx, &newy);
7537 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7540 MovDir[x][y] = old_move_dir;
7543 else if (move_pattern == MV_WHEN_PUSHED ||
7544 move_pattern == MV_WHEN_DROPPED)
7546 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7547 MovDir[x][y] = MV_NONE;
7551 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7553 static int test_xy[7][2] =
7563 static int test_dir[7] =
7573 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7574 int move_preference = -1000000; /* start with very low preference */
7575 int new_move_dir = MV_NONE;
7576 int start_test = RND(4);
7579 for (i = 0; i < NUM_DIRECTIONS; i++)
7581 int move_dir = test_dir[start_test + i];
7582 int move_dir_preference;
7584 xx = x + test_xy[start_test + i][0];
7585 yy = y + test_xy[start_test + i][1];
7587 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7588 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7590 new_move_dir = move_dir;
7595 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7598 move_dir_preference = -1 * RunnerVisit[xx][yy];
7599 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7600 move_dir_preference = PlayerVisit[xx][yy];
7602 if (move_dir_preference > move_preference)
7604 /* prefer field that has not been visited for the longest time */
7605 move_preference = move_dir_preference;
7606 new_move_dir = move_dir;
7608 else if (move_dir_preference == move_preference &&
7609 move_dir == old_move_dir)
7611 /* prefer last direction when all directions are preferred equally */
7612 move_preference = move_dir_preference;
7613 new_move_dir = move_dir;
7617 MovDir[x][y] = new_move_dir;
7618 if (old_move_dir != new_move_dir)
7619 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7623 static void TurnRound(int x, int y)
7625 int direction = MovDir[x][y];
7629 GfxDir[x][y] = MovDir[x][y];
7631 if (direction != MovDir[x][y])
7635 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7637 ResetGfxFrame(x, y, FALSE);
7640 static boolean JustBeingPushed(int x, int y)
7644 for (i = 0; i < MAX_PLAYERS; i++)
7646 struct PlayerInfo *player = &stored_player[i];
7648 if (player->active && player->is_pushing && player->MovPos)
7650 int next_jx = player->jx + (player->jx - player->last_jx);
7651 int next_jy = player->jy + (player->jy - player->last_jy);
7653 if (x == next_jx && y == next_jy)
7661 void StartMoving(int x, int y)
7663 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7664 int element = Feld[x][y];
7669 if (MovDelay[x][y] == 0)
7670 GfxAction[x][y] = ACTION_DEFAULT;
7672 if (CAN_FALL(element) && y < lev_fieldy - 1)
7674 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7675 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7676 if (JustBeingPushed(x, y))
7679 if (element == EL_QUICKSAND_FULL)
7681 if (IS_FREE(x, y + 1))
7683 InitMovingField(x, y, MV_DOWN);
7684 started_moving = TRUE;
7686 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7687 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7688 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7689 Store[x][y] = EL_ROCK;
7691 Store[x][y] = EL_ROCK;
7694 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7696 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7698 if (!MovDelay[x][y])
7700 MovDelay[x][y] = TILEY + 1;
7702 ResetGfxAnimation(x, y);
7703 ResetGfxAnimation(x, y + 1);
7708 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7709 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7716 Feld[x][y] = EL_QUICKSAND_EMPTY;
7717 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7718 Store[x][y + 1] = Store[x][y];
7721 PlayLevelSoundAction(x, y, ACTION_FILLING);
7723 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7725 if (!MovDelay[x][y])
7727 MovDelay[x][y] = TILEY + 1;
7729 ResetGfxAnimation(x, y);
7730 ResetGfxAnimation(x, y + 1);
7735 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7736 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7743 Feld[x][y] = EL_QUICKSAND_EMPTY;
7744 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7745 Store[x][y + 1] = Store[x][y];
7748 PlayLevelSoundAction(x, y, ACTION_FILLING);
7751 else if (element == EL_QUICKSAND_FAST_FULL)
7753 if (IS_FREE(x, y + 1))
7755 InitMovingField(x, y, MV_DOWN);
7756 started_moving = TRUE;
7758 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7759 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7760 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7761 Store[x][y] = EL_ROCK;
7763 Store[x][y] = EL_ROCK;
7766 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7768 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7770 if (!MovDelay[x][y])
7772 MovDelay[x][y] = TILEY + 1;
7774 ResetGfxAnimation(x, y);
7775 ResetGfxAnimation(x, y + 1);
7780 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7781 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7788 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7789 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7790 Store[x][y + 1] = Store[x][y];
7793 PlayLevelSoundAction(x, y, ACTION_FILLING);
7795 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7797 if (!MovDelay[x][y])
7799 MovDelay[x][y] = TILEY + 1;
7801 ResetGfxAnimation(x, y);
7802 ResetGfxAnimation(x, y + 1);
7807 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7808 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7815 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7816 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7817 Store[x][y + 1] = Store[x][y];
7820 PlayLevelSoundAction(x, y, ACTION_FILLING);
7823 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7824 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7826 InitMovingField(x, y, MV_DOWN);
7827 started_moving = TRUE;
7829 Feld[x][y] = EL_QUICKSAND_FILLING;
7830 Store[x][y] = element;
7832 PlayLevelSoundAction(x, y, ACTION_FILLING);
7834 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7835 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7837 InitMovingField(x, y, MV_DOWN);
7838 started_moving = TRUE;
7840 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7841 Store[x][y] = element;
7843 PlayLevelSoundAction(x, y, ACTION_FILLING);
7845 else if (element == EL_MAGIC_WALL_FULL)
7847 if (IS_FREE(x, y + 1))
7849 InitMovingField(x, y, MV_DOWN);
7850 started_moving = TRUE;
7852 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7853 Store[x][y] = EL_CHANGED(Store[x][y]);
7855 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7857 if (!MovDelay[x][y])
7858 MovDelay[x][y] = TILEY/4 + 1;
7867 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7868 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7869 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7873 else if (element == EL_BD_MAGIC_WALL_FULL)
7875 if (IS_FREE(x, y + 1))
7877 InitMovingField(x, y, MV_DOWN);
7878 started_moving = TRUE;
7880 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7881 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7883 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7885 if (!MovDelay[x][y])
7886 MovDelay[x][y] = TILEY/4 + 1;
7895 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7896 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7897 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7901 else if (element == EL_DC_MAGIC_WALL_FULL)
7903 if (IS_FREE(x, y + 1))
7905 InitMovingField(x, y, MV_DOWN);
7906 started_moving = TRUE;
7908 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7909 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7911 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7913 if (!MovDelay[x][y])
7914 MovDelay[x][y] = TILEY/4 + 1;
7923 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7924 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7925 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7929 else if ((CAN_PASS_MAGIC_WALL(element) &&
7930 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7931 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7932 (CAN_PASS_DC_MAGIC_WALL(element) &&
7933 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7936 InitMovingField(x, y, MV_DOWN);
7937 started_moving = TRUE;
7940 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7941 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7942 EL_DC_MAGIC_WALL_FILLING);
7943 Store[x][y] = element;
7945 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7947 SplashAcid(x, y + 1);
7949 InitMovingField(x, y, MV_DOWN);
7950 started_moving = TRUE;
7952 Store[x][y] = EL_ACID;
7955 #if USE_FIX_IMPACT_COLLISION
7956 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7957 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7959 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7960 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7962 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7963 CAN_FALL(element) && WasJustFalling[x][y] &&
7964 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7966 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7967 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7968 (Feld[x][y + 1] == EL_BLOCKED)))
7970 /* this is needed for a special case not covered by calling "Impact()"
7971 from "ContinueMoving()": if an element moves to a tile directly below
7972 another element which was just falling on that tile (which was empty
7973 in the previous frame), the falling element above would just stop
7974 instead of smashing the element below (in previous version, the above
7975 element was just checked for "moving" instead of "falling", resulting
7976 in incorrect smashes caused by horizontal movement of the above
7977 element; also, the case of the player being the element to smash was
7978 simply not covered here... :-/ ) */
7980 CheckCollision[x][y] = 0;
7981 CheckImpact[x][y] = 0;
7985 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7987 if (MovDir[x][y] == MV_NONE)
7989 InitMovingField(x, y, MV_DOWN);
7990 started_moving = TRUE;
7993 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7995 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7996 MovDir[x][y] = MV_DOWN;
7998 InitMovingField(x, y, MV_DOWN);
7999 started_moving = TRUE;
8001 else if (element == EL_AMOEBA_DROP)
8003 Feld[x][y] = EL_AMOEBA_GROWING;
8004 Store[x][y] = EL_AMOEBA_WET;
8006 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8007 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8008 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8009 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8011 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8012 (IS_FREE(x - 1, y + 1) ||
8013 Feld[x - 1][y + 1] == EL_ACID));
8014 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8015 (IS_FREE(x + 1, y + 1) ||
8016 Feld[x + 1][y + 1] == EL_ACID));
8017 boolean can_fall_any = (can_fall_left || can_fall_right);
8018 boolean can_fall_both = (can_fall_left && can_fall_right);
8019 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8021 #if USE_NEW_ALL_SLIPPERY
8022 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8024 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8025 can_fall_right = FALSE;
8026 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8027 can_fall_left = FALSE;
8028 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8029 can_fall_right = FALSE;
8030 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8031 can_fall_left = FALSE;
8033 can_fall_any = (can_fall_left || can_fall_right);
8034 can_fall_both = FALSE;
8037 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8039 if (slippery_type == SLIPPERY_ONLY_LEFT)
8040 can_fall_right = FALSE;
8041 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8042 can_fall_left = FALSE;
8043 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8044 can_fall_right = FALSE;
8045 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8046 can_fall_left = FALSE;
8048 can_fall_any = (can_fall_left || can_fall_right);
8049 can_fall_both = (can_fall_left && can_fall_right);
8053 #if USE_NEW_ALL_SLIPPERY
8055 #if USE_NEW_SP_SLIPPERY
8056 /* !!! better use the same properties as for custom elements here !!! */
8057 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8058 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8060 can_fall_right = FALSE; /* slip down on left side */
8061 can_fall_both = FALSE;
8066 #if USE_NEW_ALL_SLIPPERY
8069 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8070 can_fall_right = FALSE; /* slip down on left side */
8072 can_fall_left = !(can_fall_right = RND(2));
8074 can_fall_both = FALSE;
8079 if (game.emulation == EMU_BOULDERDASH ||
8080 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8081 can_fall_right = FALSE; /* slip down on left side */
8083 can_fall_left = !(can_fall_right = RND(2));
8085 can_fall_both = FALSE;
8091 /* if not determined otherwise, prefer left side for slipping down */
8092 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8093 started_moving = TRUE;
8097 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8099 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8102 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8103 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8104 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8105 int belt_dir = game.belt_dir[belt_nr];
8107 if ((belt_dir == MV_LEFT && left_is_free) ||
8108 (belt_dir == MV_RIGHT && right_is_free))
8110 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8112 InitMovingField(x, y, belt_dir);
8113 started_moving = TRUE;
8115 Pushed[x][y] = TRUE;
8116 Pushed[nextx][y] = TRUE;
8118 GfxAction[x][y] = ACTION_DEFAULT;
8122 MovDir[x][y] = 0; /* if element was moving, stop it */
8127 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8129 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8131 if (CAN_MOVE(element) && !started_moving)
8134 int move_pattern = element_info[element].move_pattern;
8139 if (MovDir[x][y] == MV_NONE)
8141 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8142 x, y, element, element_info[element].token_name);
8143 printf("StartMoving(): This should never happen!\n");
8148 Moving2Blocked(x, y, &newx, &newy);
8150 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8153 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8154 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8156 WasJustMoving[x][y] = 0;
8157 CheckCollision[x][y] = 0;
8159 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8161 if (Feld[x][y] != element) /* element has changed */
8165 if (!MovDelay[x][y]) /* start new movement phase */
8167 /* all objects that can change their move direction after each step
8168 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8170 if (element != EL_YAMYAM &&
8171 element != EL_DARK_YAMYAM &&
8172 element != EL_PACMAN &&
8173 !(move_pattern & MV_ANY_DIRECTION) &&
8174 move_pattern != MV_TURNING_LEFT &&
8175 move_pattern != MV_TURNING_RIGHT &&
8176 move_pattern != MV_TURNING_LEFT_RIGHT &&
8177 move_pattern != MV_TURNING_RIGHT_LEFT &&
8178 move_pattern != MV_TURNING_RANDOM)
8182 if (MovDelay[x][y] && (element == EL_BUG ||
8183 element == EL_SPACESHIP ||
8184 element == EL_SP_SNIKSNAK ||
8185 element == EL_SP_ELECTRON ||
8186 element == EL_MOLE))
8187 TEST_DrawLevelField(x, y);
8191 if (MovDelay[x][y]) /* wait some time before next movement */
8195 if (element == EL_ROBOT ||
8196 element == EL_YAMYAM ||
8197 element == EL_DARK_YAMYAM)
8199 DrawLevelElementAnimationIfNeeded(x, y, element);
8200 PlayLevelSoundAction(x, y, ACTION_WAITING);
8202 else if (element == EL_SP_ELECTRON)
8203 DrawLevelElementAnimationIfNeeded(x, y, element);
8204 else if (element == EL_DRAGON)
8207 int dir = MovDir[x][y];
8208 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8209 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8210 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8211 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8212 dir == MV_UP ? IMG_FLAMES_1_UP :
8213 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8214 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8216 GfxAction[x][y] = ACTION_ATTACKING;
8218 if (IS_PLAYER(x, y))
8219 DrawPlayerField(x, y);
8221 TEST_DrawLevelField(x, y);
8223 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8225 for (i = 1; i <= 3; i++)
8227 int xx = x + i * dx;
8228 int yy = y + i * dy;
8229 int sx = SCREENX(xx);
8230 int sy = SCREENY(yy);
8231 int flame_graphic = graphic + (i - 1);
8233 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8238 int flamed = MovingOrBlocked2Element(xx, yy);
8242 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8244 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8245 RemoveMovingField(xx, yy);
8247 RemoveField(xx, yy);
8249 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8252 RemoveMovingField(xx, yy);
8255 ChangeDelay[xx][yy] = 0;
8257 Feld[xx][yy] = EL_FLAMES;
8259 if (IN_SCR_FIELD(sx, sy))
8261 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8262 DrawGraphic(sx, sy, flame_graphic, frame);
8267 if (Feld[xx][yy] == EL_FLAMES)
8268 Feld[xx][yy] = EL_EMPTY;
8269 TEST_DrawLevelField(xx, yy);
8274 if (MovDelay[x][y]) /* element still has to wait some time */
8276 PlayLevelSoundAction(x, y, ACTION_WAITING);
8282 /* now make next step */
8284 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8286 if (DONT_COLLIDE_WITH(element) &&
8287 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8288 !PLAYER_ENEMY_PROTECTED(newx, newy))
8290 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8295 else if (CAN_MOVE_INTO_ACID(element) &&
8296 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8297 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8298 (MovDir[x][y] == MV_DOWN ||
8299 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8301 SplashAcid(newx, newy);
8302 Store[x][y] = EL_ACID;
8304 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8306 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8307 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8308 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8309 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8312 TEST_DrawLevelField(x, y);
8314 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8315 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8316 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8318 local_player->friends_still_needed--;
8319 if (!local_player->friends_still_needed &&
8320 !local_player->GameOver && AllPlayersGone)
8321 PlayerWins(local_player);
8325 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8327 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8328 TEST_DrawLevelField(newx, newy);
8330 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8332 else if (!IS_FREE(newx, newy))
8334 GfxAction[x][y] = ACTION_WAITING;
8336 if (IS_PLAYER(x, y))
8337 DrawPlayerField(x, y);
8339 TEST_DrawLevelField(x, y);
8344 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8346 if (IS_FOOD_PIG(Feld[newx][newy]))
8348 if (IS_MOVING(newx, newy))
8349 RemoveMovingField(newx, newy);
8352 Feld[newx][newy] = EL_EMPTY;
8353 TEST_DrawLevelField(newx, newy);
8356 PlayLevelSound(x, y, SND_PIG_DIGGING);
8358 else if (!IS_FREE(newx, newy))
8360 if (IS_PLAYER(x, y))
8361 DrawPlayerField(x, y);
8363 TEST_DrawLevelField(x, y);
8368 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8370 if (Store[x][y] != EL_EMPTY)
8372 boolean can_clone = FALSE;
8375 /* check if element to clone is still there */
8376 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8378 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8386 /* cannot clone or target field not free anymore -- do not clone */
8387 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8388 Store[x][y] = EL_EMPTY;
8391 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8393 if (IS_MV_DIAGONAL(MovDir[x][y]))
8395 int diagonal_move_dir = MovDir[x][y];
8396 int stored = Store[x][y];
8397 int change_delay = 8;
8400 /* android is moving diagonally */
8402 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8404 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8405 GfxElement[x][y] = EL_EMC_ANDROID;
8406 GfxAction[x][y] = ACTION_SHRINKING;
8407 GfxDir[x][y] = diagonal_move_dir;
8408 ChangeDelay[x][y] = change_delay;
8410 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8413 DrawLevelGraphicAnimation(x, y, graphic);
8414 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8416 if (Feld[newx][newy] == EL_ACID)
8418 SplashAcid(newx, newy);
8423 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8425 Store[newx][newy] = EL_EMC_ANDROID;
8426 GfxElement[newx][newy] = EL_EMC_ANDROID;
8427 GfxAction[newx][newy] = ACTION_GROWING;
8428 GfxDir[newx][newy] = diagonal_move_dir;
8429 ChangeDelay[newx][newy] = change_delay;
8431 graphic = el_act_dir2img(GfxElement[newx][newy],
8432 GfxAction[newx][newy], GfxDir[newx][newy]);
8434 DrawLevelGraphicAnimation(newx, newy, graphic);
8435 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8441 Feld[newx][newy] = EL_EMPTY;
8442 TEST_DrawLevelField(newx, newy);
8444 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8447 else if (!IS_FREE(newx, newy))
8450 if (IS_PLAYER(x, y))
8451 DrawPlayerField(x, y);
8453 TEST_DrawLevelField(x, y);
8459 else if (IS_CUSTOM_ELEMENT(element) &&
8460 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8463 if (!DigFieldByCE(newx, newy, element))
8466 int new_element = Feld[newx][newy];
8468 if (!IS_FREE(newx, newy))
8470 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8471 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8474 /* no element can dig solid indestructible elements */
8475 if (IS_INDESTRUCTIBLE(new_element) &&
8476 !IS_DIGGABLE(new_element) &&
8477 !IS_COLLECTIBLE(new_element))
8480 if (AmoebaNr[newx][newy] &&
8481 (new_element == EL_AMOEBA_FULL ||
8482 new_element == EL_BD_AMOEBA ||
8483 new_element == EL_AMOEBA_GROWING))
8485 AmoebaCnt[AmoebaNr[newx][newy]]--;
8486 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8489 if (IS_MOVING(newx, newy))
8490 RemoveMovingField(newx, newy);
8493 RemoveField(newx, newy);
8494 TEST_DrawLevelField(newx, newy);
8497 /* if digged element was about to explode, prevent the explosion */
8498 ExplodeField[newx][newy] = EX_TYPE_NONE;
8500 PlayLevelSoundAction(x, y, action);
8503 Store[newx][newy] = EL_EMPTY;
8506 /* this makes it possible to leave the removed element again */
8507 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8508 Store[newx][newy] = new_element;
8510 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8512 int move_leave_element = element_info[element].move_leave_element;
8514 /* this makes it possible to leave the removed element again */
8515 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8516 new_element : move_leave_element);
8522 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8524 RunnerVisit[x][y] = FrameCounter;
8525 PlayerVisit[x][y] /= 8; /* expire player visit path */
8528 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8530 if (!IS_FREE(newx, newy))
8532 if (IS_PLAYER(x, y))
8533 DrawPlayerField(x, y);
8535 TEST_DrawLevelField(x, y);
8541 boolean wanna_flame = !RND(10);
8542 int dx = newx - x, dy = newy - y;
8543 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8544 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8545 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8546 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8547 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8548 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8551 IS_CLASSIC_ENEMY(element1) ||
8552 IS_CLASSIC_ENEMY(element2)) &&
8553 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8554 element1 != EL_FLAMES && element2 != EL_FLAMES)
8556 ResetGfxAnimation(x, y);
8557 GfxAction[x][y] = ACTION_ATTACKING;
8559 if (IS_PLAYER(x, y))
8560 DrawPlayerField(x, y);
8562 TEST_DrawLevelField(x, y);
8564 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8566 MovDelay[x][y] = 50;
8570 RemoveField(newx, newy);
8572 Feld[newx][newy] = EL_FLAMES;
8573 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8576 RemoveField(newx1, newy1);
8578 Feld[newx1][newy1] = EL_FLAMES;
8580 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8583 RemoveField(newx2, newy2);
8585 Feld[newx2][newy2] = EL_FLAMES;
8592 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8593 Feld[newx][newy] == EL_DIAMOND)
8595 if (IS_MOVING(newx, newy))
8596 RemoveMovingField(newx, newy);
8599 Feld[newx][newy] = EL_EMPTY;
8600 TEST_DrawLevelField(newx, newy);
8603 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8605 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8606 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8608 if (AmoebaNr[newx][newy])
8610 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8611 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8612 Feld[newx][newy] == EL_BD_AMOEBA)
8613 AmoebaCnt[AmoebaNr[newx][newy]]--;
8618 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8620 RemoveMovingField(newx, newy);
8623 if (IS_MOVING(newx, newy))
8625 RemoveMovingField(newx, newy);
8630 Feld[newx][newy] = EL_EMPTY;
8631 TEST_DrawLevelField(newx, newy);
8634 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8636 else if ((element == EL_PACMAN || element == EL_MOLE)
8637 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8639 if (AmoebaNr[newx][newy])
8641 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8642 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8643 Feld[newx][newy] == EL_BD_AMOEBA)
8644 AmoebaCnt[AmoebaNr[newx][newy]]--;
8647 if (element == EL_MOLE)
8649 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8650 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8652 ResetGfxAnimation(x, y);
8653 GfxAction[x][y] = ACTION_DIGGING;
8654 TEST_DrawLevelField(x, y);
8656 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8658 return; /* wait for shrinking amoeba */
8660 else /* element == EL_PACMAN */
8662 Feld[newx][newy] = EL_EMPTY;
8663 TEST_DrawLevelField(newx, newy);
8664 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8667 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8668 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8669 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8671 /* wait for shrinking amoeba to completely disappear */
8674 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8676 /* object was running against a wall */
8681 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8682 if (move_pattern & MV_ANY_DIRECTION &&
8683 move_pattern == MovDir[x][y])
8685 int blocking_element =
8686 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8688 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8691 element = Feld[x][y]; /* element might have changed */
8695 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8696 DrawLevelElementAnimation(x, y, element);
8698 if (DONT_TOUCH(element))
8699 TestIfBadThingTouchesPlayer(x, y);
8704 InitMovingField(x, y, MovDir[x][y]);
8706 PlayLevelSoundAction(x, y, ACTION_MOVING);
8710 ContinueMoving(x, y);
8713 void ContinueMoving(int x, int y)
8715 int element = Feld[x][y];
8716 struct ElementInfo *ei = &element_info[element];
8717 int direction = MovDir[x][y];
8718 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8719 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8720 int newx = x + dx, newy = y + dy;
8721 int stored = Store[x][y];
8722 int stored_new = Store[newx][newy];
8723 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8724 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8725 boolean last_line = (newy == lev_fieldy - 1);
8727 MovPos[x][y] += getElementMoveStepsize(x, y);
8729 if (pushed_by_player) /* special case: moving object pushed by player */
8730 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8732 if (ABS(MovPos[x][y]) < TILEX)
8735 int ee = Feld[x][y];
8736 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8737 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8739 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8740 x, y, ABS(MovPos[x][y]),
8742 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8745 TEST_DrawLevelField(x, y);
8747 return; /* element is still moving */
8750 /* element reached destination field */
8752 Feld[x][y] = EL_EMPTY;
8753 Feld[newx][newy] = element;
8754 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8756 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8758 element = Feld[newx][newy] = EL_ACID;
8760 else if (element == EL_MOLE)
8762 Feld[x][y] = EL_SAND;
8764 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8766 else if (element == EL_QUICKSAND_FILLING)
8768 element = Feld[newx][newy] = get_next_element(element);
8769 Store[newx][newy] = Store[x][y];
8771 else if (element == EL_QUICKSAND_EMPTYING)
8773 Feld[x][y] = get_next_element(element);
8774 element = Feld[newx][newy] = Store[x][y];
8776 else if (element == EL_QUICKSAND_FAST_FILLING)
8778 element = Feld[newx][newy] = get_next_element(element);
8779 Store[newx][newy] = Store[x][y];
8781 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8783 Feld[x][y] = get_next_element(element);
8784 element = Feld[newx][newy] = Store[x][y];
8786 else if (element == EL_MAGIC_WALL_FILLING)
8788 element = Feld[newx][newy] = get_next_element(element);
8789 if (!game.magic_wall_active)
8790 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8791 Store[newx][newy] = Store[x][y];
8793 else if (element == EL_MAGIC_WALL_EMPTYING)
8795 Feld[x][y] = get_next_element(element);
8796 if (!game.magic_wall_active)
8797 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8798 element = Feld[newx][newy] = Store[x][y];
8800 #if USE_NEW_CUSTOM_VALUE
8801 InitField(newx, newy, FALSE);
8804 else if (element == EL_BD_MAGIC_WALL_FILLING)
8806 element = Feld[newx][newy] = get_next_element(element);
8807 if (!game.magic_wall_active)
8808 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8809 Store[newx][newy] = Store[x][y];
8811 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8813 Feld[x][y] = get_next_element(element);
8814 if (!game.magic_wall_active)
8815 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8816 element = Feld[newx][newy] = Store[x][y];
8818 #if USE_NEW_CUSTOM_VALUE
8819 InitField(newx, newy, FALSE);
8822 else if (element == EL_DC_MAGIC_WALL_FILLING)
8824 element = Feld[newx][newy] = get_next_element(element);
8825 if (!game.magic_wall_active)
8826 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8827 Store[newx][newy] = Store[x][y];
8829 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8831 Feld[x][y] = get_next_element(element);
8832 if (!game.magic_wall_active)
8833 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8834 element = Feld[newx][newy] = Store[x][y];
8836 #if USE_NEW_CUSTOM_VALUE
8837 InitField(newx, newy, FALSE);
8840 else if (element == EL_AMOEBA_DROPPING)
8842 Feld[x][y] = get_next_element(element);
8843 element = Feld[newx][newy] = Store[x][y];
8845 else if (element == EL_SOKOBAN_OBJECT)
8848 Feld[x][y] = Back[x][y];
8850 if (Back[newx][newy])
8851 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8853 Back[x][y] = Back[newx][newy] = 0;
8856 Store[x][y] = EL_EMPTY;
8861 MovDelay[newx][newy] = 0;
8863 if (CAN_CHANGE_OR_HAS_ACTION(element))
8865 /* copy element change control values to new field */
8866 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8867 ChangePage[newx][newy] = ChangePage[x][y];
8868 ChangeCount[newx][newy] = ChangeCount[x][y];
8869 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8872 #if USE_NEW_CUSTOM_VALUE
8873 CustomValue[newx][newy] = CustomValue[x][y];
8876 ChangeDelay[x][y] = 0;
8877 ChangePage[x][y] = -1;
8878 ChangeCount[x][y] = 0;
8879 ChangeEvent[x][y] = -1;
8881 #if USE_NEW_CUSTOM_VALUE
8882 CustomValue[x][y] = 0;
8885 /* copy animation control values to new field */
8886 GfxFrame[newx][newy] = GfxFrame[x][y];
8887 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8888 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8889 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8891 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8893 /* some elements can leave other elements behind after moving */
8895 if (ei->move_leave_element != EL_EMPTY &&
8896 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8897 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8899 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8900 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8901 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8904 int move_leave_element = ei->move_leave_element;
8908 /* this makes it possible to leave the removed element again */
8909 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8910 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8912 /* this makes it possible to leave the removed element again */
8913 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8914 move_leave_element = stored;
8917 /* this makes it possible to leave the removed element again */
8918 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8919 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8920 move_leave_element = stored;
8923 Feld[x][y] = move_leave_element;
8925 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8926 MovDir[x][y] = direction;
8928 InitField(x, y, FALSE);
8930 if (GFX_CRUMBLED(Feld[x][y]))
8931 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8933 if (ELEM_IS_PLAYER(move_leave_element))
8934 RelocatePlayer(x, y, move_leave_element);
8937 /* do this after checking for left-behind element */
8938 ResetGfxAnimation(x, y); /* reset animation values for old field */
8940 if (!CAN_MOVE(element) ||
8941 (CAN_FALL(element) && direction == MV_DOWN &&
8942 (element == EL_SPRING ||
8943 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8944 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8945 GfxDir[x][y] = MovDir[newx][newy] = 0;
8947 TEST_DrawLevelField(x, y);
8948 TEST_DrawLevelField(newx, newy);
8950 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8952 /* prevent pushed element from moving on in pushed direction */
8953 if (pushed_by_player && CAN_MOVE(element) &&
8954 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8955 !(element_info[element].move_pattern & direction))
8956 TurnRound(newx, newy);
8958 /* prevent elements on conveyor belt from moving on in last direction */
8959 if (pushed_by_conveyor && CAN_FALL(element) &&
8960 direction & MV_HORIZONTAL)
8961 MovDir[newx][newy] = 0;
8963 if (!pushed_by_player)
8965 int nextx = newx + dx, nexty = newy + dy;
8966 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8968 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8970 if (CAN_FALL(element) && direction == MV_DOWN)
8971 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8973 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8974 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8976 #if USE_FIX_IMPACT_COLLISION
8977 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8978 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8982 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8984 TestIfBadThingTouchesPlayer(newx, newy);
8985 TestIfBadThingTouchesFriend(newx, newy);
8987 if (!IS_CUSTOM_ELEMENT(element))
8988 TestIfBadThingTouchesOtherBadThing(newx, newy);
8990 else if (element == EL_PENGUIN)
8991 TestIfFriendTouchesBadThing(newx, newy);
8993 if (DONT_GET_HIT_BY(element))
8995 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8998 /* give the player one last chance (one more frame) to move away */
8999 if (CAN_FALL(element) && direction == MV_DOWN &&
9000 (last_line || (!IS_FREE(x, newy + 1) &&
9001 (!IS_PLAYER(x, newy + 1) ||
9002 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9005 if (pushed_by_player && !game.use_change_when_pushing_bug)
9007 int push_side = MV_DIR_OPPOSITE(direction);
9008 struct PlayerInfo *player = PLAYERINFO(x, y);
9010 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9011 player->index_bit, push_side);
9012 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9013 player->index_bit, push_side);
9016 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9017 MovDelay[newx][newy] = 1;
9019 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9021 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9024 if (ChangePage[newx][newy] != -1) /* delayed change */
9026 int page = ChangePage[newx][newy];
9027 struct ElementChangeInfo *change = &ei->change_page[page];
9029 ChangePage[newx][newy] = -1;
9031 if (change->can_change)
9033 if (ChangeElement(newx, newy, element, page))
9035 if (change->post_change_function)
9036 change->post_change_function(newx, newy);
9040 if (change->has_action)
9041 ExecuteCustomElementAction(newx, newy, element, page);
9045 TestIfElementHitsCustomElement(newx, newy, direction);
9046 TestIfPlayerTouchesCustomElement(newx, newy);
9047 TestIfElementTouchesCustomElement(newx, newy);
9049 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9050 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9051 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9052 MV_DIR_OPPOSITE(direction));
9055 int AmoebeNachbarNr(int ax, int ay)
9058 int element = Feld[ax][ay];
9060 static int xy[4][2] =
9068 for (i = 0; i < NUM_DIRECTIONS; i++)
9070 int x = ax + xy[i][0];
9071 int y = ay + xy[i][1];
9073 if (!IN_LEV_FIELD(x, y))
9076 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9077 group_nr = AmoebaNr[x][y];
9083 void AmoebenVereinigen(int ax, int ay)
9085 int i, x, y, xx, yy;
9086 int new_group_nr = AmoebaNr[ax][ay];
9087 static int xy[4][2] =
9095 if (new_group_nr == 0)
9098 for (i = 0; i < NUM_DIRECTIONS; i++)
9103 if (!IN_LEV_FIELD(x, y))
9106 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9107 Feld[x][y] == EL_BD_AMOEBA ||
9108 Feld[x][y] == EL_AMOEBA_DEAD) &&
9109 AmoebaNr[x][y] != new_group_nr)
9111 int old_group_nr = AmoebaNr[x][y];
9113 if (old_group_nr == 0)
9116 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9117 AmoebaCnt[old_group_nr] = 0;
9118 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9119 AmoebaCnt2[old_group_nr] = 0;
9121 SCAN_PLAYFIELD(xx, yy)
9123 if (AmoebaNr[xx][yy] == old_group_nr)
9124 AmoebaNr[xx][yy] = new_group_nr;
9130 void AmoebeUmwandeln(int ax, int ay)
9134 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9136 int group_nr = AmoebaNr[ax][ay];
9141 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9142 printf("AmoebeUmwandeln(): This should never happen!\n");
9147 SCAN_PLAYFIELD(x, y)
9149 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9152 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9156 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9157 SND_AMOEBA_TURNING_TO_GEM :
9158 SND_AMOEBA_TURNING_TO_ROCK));
9163 static int xy[4][2] =
9171 for (i = 0; i < NUM_DIRECTIONS; i++)
9176 if (!IN_LEV_FIELD(x, y))
9179 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9181 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9182 SND_AMOEBA_TURNING_TO_GEM :
9183 SND_AMOEBA_TURNING_TO_ROCK));
9190 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9193 int group_nr = AmoebaNr[ax][ay];
9194 boolean done = FALSE;
9199 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9200 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9205 SCAN_PLAYFIELD(x, y)
9207 if (AmoebaNr[x][y] == group_nr &&
9208 (Feld[x][y] == EL_AMOEBA_DEAD ||
9209 Feld[x][y] == EL_BD_AMOEBA ||
9210 Feld[x][y] == EL_AMOEBA_GROWING))
9213 Feld[x][y] = new_element;
9214 InitField(x, y, FALSE);
9215 TEST_DrawLevelField(x, y);
9221 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9222 SND_BD_AMOEBA_TURNING_TO_ROCK :
9223 SND_BD_AMOEBA_TURNING_TO_GEM));
9226 void AmoebeWaechst(int x, int y)
9228 static unsigned long sound_delay = 0;
9229 static unsigned long sound_delay_value = 0;
9231 if (!MovDelay[x][y]) /* start new growing cycle */
9235 if (DelayReached(&sound_delay, sound_delay_value))
9237 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9238 sound_delay_value = 30;
9242 if (MovDelay[x][y]) /* wait some time before growing bigger */
9245 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9247 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9248 6 - MovDelay[x][y]);
9250 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9253 if (!MovDelay[x][y])
9255 Feld[x][y] = Store[x][y];
9257 TEST_DrawLevelField(x, y);
9262 void AmoebaDisappearing(int x, int y)
9264 static unsigned long sound_delay = 0;
9265 static unsigned long sound_delay_value = 0;
9267 if (!MovDelay[x][y]) /* start new shrinking cycle */
9271 if (DelayReached(&sound_delay, sound_delay_value))
9272 sound_delay_value = 30;
9275 if (MovDelay[x][y]) /* wait some time before shrinking */
9278 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9280 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9281 6 - MovDelay[x][y]);
9283 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9286 if (!MovDelay[x][y])
9288 Feld[x][y] = EL_EMPTY;
9289 TEST_DrawLevelField(x, y);
9291 /* don't let mole enter this field in this cycle;
9292 (give priority to objects falling to this field from above) */
9298 void AmoebeAbleger(int ax, int ay)
9301 int element = Feld[ax][ay];
9302 int graphic = el2img(element);
9303 int newax = ax, neway = ay;
9304 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9305 static int xy[4][2] =
9313 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9315 Feld[ax][ay] = EL_AMOEBA_DEAD;
9316 TEST_DrawLevelField(ax, ay);
9320 if (IS_ANIMATED(graphic))
9321 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9323 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9324 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9326 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9329 if (MovDelay[ax][ay])
9333 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9336 int x = ax + xy[start][0];
9337 int y = ay + xy[start][1];
9339 if (!IN_LEV_FIELD(x, y))
9342 if (IS_FREE(x, y) ||
9343 CAN_GROW_INTO(Feld[x][y]) ||
9344 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9345 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9351 if (newax == ax && neway == ay)
9354 else /* normal or "filled" (BD style) amoeba */
9357 boolean waiting_for_player = FALSE;
9359 for (i = 0; i < NUM_DIRECTIONS; i++)
9361 int j = (start + i) % 4;
9362 int x = ax + xy[j][0];
9363 int y = ay + xy[j][1];
9365 if (!IN_LEV_FIELD(x, y))
9368 if (IS_FREE(x, y) ||
9369 CAN_GROW_INTO(Feld[x][y]) ||
9370 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9371 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9377 else if (IS_PLAYER(x, y))
9378 waiting_for_player = TRUE;
9381 if (newax == ax && neway == ay) /* amoeba cannot grow */
9383 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9385 Feld[ax][ay] = EL_AMOEBA_DEAD;
9386 TEST_DrawLevelField(ax, ay);
9387 AmoebaCnt[AmoebaNr[ax][ay]]--;
9389 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9391 if (element == EL_AMOEBA_FULL)
9392 AmoebeUmwandeln(ax, ay);
9393 else if (element == EL_BD_AMOEBA)
9394 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9399 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9401 /* amoeba gets larger by growing in some direction */
9403 int new_group_nr = AmoebaNr[ax][ay];
9406 if (new_group_nr == 0)
9408 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9409 printf("AmoebeAbleger(): This should never happen!\n");
9414 AmoebaNr[newax][neway] = new_group_nr;
9415 AmoebaCnt[new_group_nr]++;
9416 AmoebaCnt2[new_group_nr]++;
9418 /* if amoeba touches other amoeba(s) after growing, unify them */
9419 AmoebenVereinigen(newax, neway);
9421 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9423 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9429 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9430 (neway == lev_fieldy - 1 && newax != ax))
9432 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9433 Store[newax][neway] = element;
9435 else if (neway == ay || element == EL_EMC_DRIPPER)
9437 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9439 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9443 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9444 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9445 Store[ax][ay] = EL_AMOEBA_DROP;
9446 ContinueMoving(ax, ay);
9450 TEST_DrawLevelField(newax, neway);
9453 void Life(int ax, int ay)
9457 int element = Feld[ax][ay];
9458 int graphic = el2img(element);
9459 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9461 boolean changed = FALSE;
9463 if (IS_ANIMATED(graphic))
9464 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9469 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9470 MovDelay[ax][ay] = life_time;
9472 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9475 if (MovDelay[ax][ay])
9479 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9481 int xx = ax+x1, yy = ay+y1;
9484 if (!IN_LEV_FIELD(xx, yy))
9487 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9489 int x = xx+x2, y = yy+y2;
9491 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9494 if (((Feld[x][y] == element ||
9495 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9497 (IS_FREE(x, y) && Stop[x][y]))
9501 if (xx == ax && yy == ay) /* field in the middle */
9503 if (nachbarn < life_parameter[0] ||
9504 nachbarn > life_parameter[1])
9506 Feld[xx][yy] = EL_EMPTY;
9508 TEST_DrawLevelField(xx, yy);
9509 Stop[xx][yy] = TRUE;
9513 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9514 { /* free border field */
9515 if (nachbarn >= life_parameter[2] &&
9516 nachbarn <= life_parameter[3])
9518 Feld[xx][yy] = element;
9519 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9521 TEST_DrawLevelField(xx, yy);
9522 Stop[xx][yy] = TRUE;
9529 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9530 SND_GAME_OF_LIFE_GROWING);
9533 static void InitRobotWheel(int x, int y)
9535 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9538 static void RunRobotWheel(int x, int y)
9540 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9543 static void StopRobotWheel(int x, int y)
9545 if (ZX == x && ZY == y)
9549 game.robot_wheel_active = FALSE;
9553 static void InitTimegateWheel(int x, int y)
9555 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9558 static void RunTimegateWheel(int x, int y)
9560 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9563 static void InitMagicBallDelay(int x, int y)
9566 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9568 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9572 static void ActivateMagicBall(int bx, int by)
9576 if (level.ball_random)
9578 int pos_border = RND(8); /* select one of the eight border elements */
9579 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9580 int xx = pos_content % 3;
9581 int yy = pos_content / 3;
9586 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9587 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9591 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9593 int xx = x - bx + 1;
9594 int yy = y - by + 1;
9596 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9597 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9601 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9604 void CheckExit(int x, int y)
9606 if (local_player->gems_still_needed > 0 ||
9607 local_player->sokobanfields_still_needed > 0 ||
9608 local_player->lights_still_needed > 0)
9610 int element = Feld[x][y];
9611 int graphic = el2img(element);
9613 if (IS_ANIMATED(graphic))
9614 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9619 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9622 Feld[x][y] = EL_EXIT_OPENING;
9624 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9627 void CheckExitEM(int x, int y)
9629 if (local_player->gems_still_needed > 0 ||
9630 local_player->sokobanfields_still_needed > 0 ||
9631 local_player->lights_still_needed > 0)
9633 int element = Feld[x][y];
9634 int graphic = el2img(element);
9636 if (IS_ANIMATED(graphic))
9637 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9642 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9645 Feld[x][y] = EL_EM_EXIT_OPENING;
9647 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9650 void CheckExitSteel(int x, int y)
9652 if (local_player->gems_still_needed > 0 ||
9653 local_player->sokobanfields_still_needed > 0 ||
9654 local_player->lights_still_needed > 0)
9656 int element = Feld[x][y];
9657 int graphic = el2img(element);
9659 if (IS_ANIMATED(graphic))
9660 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9665 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9668 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9670 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9673 void CheckExitSteelEM(int x, int y)
9675 if (local_player->gems_still_needed > 0 ||
9676 local_player->sokobanfields_still_needed > 0 ||
9677 local_player->lights_still_needed > 0)
9679 int element = Feld[x][y];
9680 int graphic = el2img(element);
9682 if (IS_ANIMATED(graphic))
9683 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9688 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9691 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9693 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9696 void CheckExitSP(int x, int y)
9698 if (local_player->gems_still_needed > 0)
9700 int element = Feld[x][y];
9701 int graphic = el2img(element);
9703 if (IS_ANIMATED(graphic))
9704 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9709 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9712 Feld[x][y] = EL_SP_EXIT_OPENING;
9714 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9717 static void CloseAllOpenTimegates()
9721 SCAN_PLAYFIELD(x, y)
9723 int element = Feld[x][y];
9725 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9727 Feld[x][y] = EL_TIMEGATE_CLOSING;
9729 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9734 void DrawTwinkleOnField(int x, int y)
9736 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9739 if (Feld[x][y] == EL_BD_DIAMOND)
9742 if (MovDelay[x][y] == 0) /* next animation frame */
9743 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9745 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9749 DrawLevelElementAnimation(x, y, Feld[x][y]);
9751 if (MovDelay[x][y] != 0)
9753 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9754 10 - MovDelay[x][y]);
9756 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9761 void MauerWaechst(int x, int y)
9765 if (!MovDelay[x][y]) /* next animation frame */
9766 MovDelay[x][y] = 3 * delay;
9768 if (MovDelay[x][y]) /* wait some time before next frame */
9772 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9774 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9775 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9777 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9780 if (!MovDelay[x][y])
9782 if (MovDir[x][y] == MV_LEFT)
9784 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9785 TEST_DrawLevelField(x - 1, y);
9787 else if (MovDir[x][y] == MV_RIGHT)
9789 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9790 TEST_DrawLevelField(x + 1, y);
9792 else if (MovDir[x][y] == MV_UP)
9794 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9795 TEST_DrawLevelField(x, y - 1);
9799 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9800 TEST_DrawLevelField(x, y + 1);
9803 Feld[x][y] = Store[x][y];
9805 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9806 TEST_DrawLevelField(x, y);
9811 void MauerAbleger(int ax, int ay)
9813 int element = Feld[ax][ay];
9814 int graphic = el2img(element);
9815 boolean oben_frei = FALSE, unten_frei = FALSE;
9816 boolean links_frei = FALSE, rechts_frei = FALSE;
9817 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9818 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9819 boolean new_wall = FALSE;
9821 if (IS_ANIMATED(graphic))
9822 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9824 if (!MovDelay[ax][ay]) /* start building new wall */
9825 MovDelay[ax][ay] = 6;
9827 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9830 if (MovDelay[ax][ay])
9834 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9836 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9838 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9840 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9843 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9844 element == EL_EXPANDABLE_WALL_ANY)
9848 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9849 Store[ax][ay-1] = element;
9850 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9851 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9852 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9853 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9858 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9859 Store[ax][ay+1] = element;
9860 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9861 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9862 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9863 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9868 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9869 element == EL_EXPANDABLE_WALL_ANY ||
9870 element == EL_EXPANDABLE_WALL ||
9871 element == EL_BD_EXPANDABLE_WALL)
9875 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9876 Store[ax-1][ay] = element;
9877 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9878 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9879 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9880 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9886 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9887 Store[ax+1][ay] = element;
9888 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9889 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9890 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9891 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9896 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9897 TEST_DrawLevelField(ax, ay);
9899 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9901 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9902 unten_massiv = TRUE;
9903 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9904 links_massiv = TRUE;
9905 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9906 rechts_massiv = TRUE;
9908 if (((oben_massiv && unten_massiv) ||
9909 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9910 element == EL_EXPANDABLE_WALL) &&
9911 ((links_massiv && rechts_massiv) ||
9912 element == EL_EXPANDABLE_WALL_VERTICAL))
9913 Feld[ax][ay] = EL_WALL;
9916 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9919 void MauerAblegerStahl(int ax, int ay)
9921 int element = Feld[ax][ay];
9922 int graphic = el2img(element);
9923 boolean oben_frei = FALSE, unten_frei = FALSE;
9924 boolean links_frei = FALSE, rechts_frei = FALSE;
9925 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9926 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9927 boolean new_wall = FALSE;
9929 if (IS_ANIMATED(graphic))
9930 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9932 if (!MovDelay[ax][ay]) /* start building new wall */
9933 MovDelay[ax][ay] = 6;
9935 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9938 if (MovDelay[ax][ay])
9942 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9944 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9946 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9948 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9951 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9952 element == EL_EXPANDABLE_STEELWALL_ANY)
9956 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9957 Store[ax][ay-1] = element;
9958 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9959 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9960 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9961 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9966 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9967 Store[ax][ay+1] = element;
9968 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9969 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9970 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9971 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9976 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9977 element == EL_EXPANDABLE_STEELWALL_ANY)
9981 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9982 Store[ax-1][ay] = element;
9983 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9984 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9985 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9986 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9992 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9993 Store[ax+1][ay] = element;
9994 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9995 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9996 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9997 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10002 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10003 oben_massiv = TRUE;
10004 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10005 unten_massiv = TRUE;
10006 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10007 links_massiv = TRUE;
10008 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10009 rechts_massiv = TRUE;
10011 if (((oben_massiv && unten_massiv) ||
10012 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10013 ((links_massiv && rechts_massiv) ||
10014 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10015 Feld[ax][ay] = EL_STEELWALL;
10018 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10021 void CheckForDragon(int x, int y)
10024 boolean dragon_found = FALSE;
10025 static int xy[4][2] =
10033 for (i = 0; i < NUM_DIRECTIONS; i++)
10035 for (j = 0; j < 4; j++)
10037 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10039 if (IN_LEV_FIELD(xx, yy) &&
10040 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10042 if (Feld[xx][yy] == EL_DRAGON)
10043 dragon_found = TRUE;
10052 for (i = 0; i < NUM_DIRECTIONS; i++)
10054 for (j = 0; j < 3; j++)
10056 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10058 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10060 Feld[xx][yy] = EL_EMPTY;
10061 TEST_DrawLevelField(xx, yy);
10070 static void InitBuggyBase(int x, int y)
10072 int element = Feld[x][y];
10073 int activating_delay = FRAMES_PER_SECOND / 4;
10075 ChangeDelay[x][y] =
10076 (element == EL_SP_BUGGY_BASE ?
10077 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10078 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10080 element == EL_SP_BUGGY_BASE_ACTIVE ?
10081 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10084 static void WarnBuggyBase(int x, int y)
10087 static int xy[4][2] =
10095 for (i = 0; i < NUM_DIRECTIONS; i++)
10097 int xx = x + xy[i][0];
10098 int yy = y + xy[i][1];
10100 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10102 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10109 static void InitTrap(int x, int y)
10111 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10114 static void ActivateTrap(int x, int y)
10116 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10119 static void ChangeActiveTrap(int x, int y)
10121 int graphic = IMG_TRAP_ACTIVE;
10123 /* if new animation frame was drawn, correct crumbled sand border */
10124 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10125 TEST_DrawLevelFieldCrumbledSand(x, y);
10128 static int getSpecialActionElement(int element, int number, int base_element)
10130 return (element != EL_EMPTY ? element :
10131 number != -1 ? base_element + number - 1 :
10135 static int getModifiedActionNumber(int value_old, int operator, int operand,
10136 int value_min, int value_max)
10138 int value_new = (operator == CA_MODE_SET ? operand :
10139 operator == CA_MODE_ADD ? value_old + operand :
10140 operator == CA_MODE_SUBTRACT ? value_old - operand :
10141 operator == CA_MODE_MULTIPLY ? value_old * operand :
10142 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10143 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10146 return (value_new < value_min ? value_min :
10147 value_new > value_max ? value_max :
10151 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10153 struct ElementInfo *ei = &element_info[element];
10154 struct ElementChangeInfo *change = &ei->change_page[page];
10155 int target_element = change->target_element;
10156 int action_type = change->action_type;
10157 int action_mode = change->action_mode;
10158 int action_arg = change->action_arg;
10159 int action_element = change->action_element;
10162 if (!change->has_action)
10165 /* ---------- determine action paramater values -------------------------- */
10167 int level_time_value =
10168 (level.time > 0 ? TimeLeft :
10171 int action_arg_element =
10172 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10173 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10174 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10175 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10178 int action_arg_direction =
10179 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10180 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10181 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10182 change->actual_trigger_side :
10183 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10184 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10187 int action_arg_number_min =
10188 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10191 int action_arg_number_max =
10192 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10193 action_type == CA_SET_LEVEL_GEMS ? 999 :
10194 action_type == CA_SET_LEVEL_TIME ? 9999 :
10195 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10196 action_type == CA_SET_CE_VALUE ? 9999 :
10197 action_type == CA_SET_CE_SCORE ? 9999 :
10200 int action_arg_number_reset =
10201 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10202 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10203 action_type == CA_SET_LEVEL_TIME ? level.time :
10204 action_type == CA_SET_LEVEL_SCORE ? 0 :
10205 #if USE_NEW_CUSTOM_VALUE
10206 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10208 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10210 action_type == CA_SET_CE_SCORE ? 0 :
10213 int action_arg_number =
10214 (action_arg <= CA_ARG_MAX ? action_arg :
10215 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10216 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10217 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10218 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10219 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10220 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10221 #if USE_NEW_CUSTOM_VALUE
10222 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10224 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10226 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10227 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10228 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10229 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10230 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10231 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10232 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10233 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10234 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10235 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10236 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10237 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10238 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10239 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10242 int action_arg_number_old =
10243 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10244 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10245 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10246 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10247 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10250 int action_arg_number_new =
10251 getModifiedActionNumber(action_arg_number_old,
10252 action_mode, action_arg_number,
10253 action_arg_number_min, action_arg_number_max);
10256 int trigger_player_bits = change->actual_trigger_player_bits;
10258 int trigger_player_bits =
10259 (change->actual_trigger_player >= EL_PLAYER_1 &&
10260 change->actual_trigger_player <= EL_PLAYER_4 ?
10261 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10265 int action_arg_player_bits =
10266 (action_arg >= CA_ARG_PLAYER_1 &&
10267 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10268 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10269 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10272 /* ---------- execute action -------------------------------------------- */
10274 switch (action_type)
10281 /* ---------- level actions ------------------------------------------- */
10283 case CA_RESTART_LEVEL:
10285 game.restart_level = TRUE;
10290 case CA_SHOW_ENVELOPE:
10292 int element = getSpecialActionElement(action_arg_element,
10293 action_arg_number, EL_ENVELOPE_1);
10295 if (IS_ENVELOPE(element))
10296 local_player->show_envelope = element;
10301 case CA_SET_LEVEL_TIME:
10303 if (level.time > 0) /* only modify limited time value */
10305 TimeLeft = action_arg_number_new;
10308 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10310 DisplayGameControlValues();
10312 DrawGameValue_Time(TimeLeft);
10315 if (!TimeLeft && setup.time_limit)
10316 for (i = 0; i < MAX_PLAYERS; i++)
10317 KillPlayer(&stored_player[i]);
10323 case CA_SET_LEVEL_SCORE:
10325 local_player->score = action_arg_number_new;
10328 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10330 DisplayGameControlValues();
10332 DrawGameValue_Score(local_player->score);
10338 case CA_SET_LEVEL_GEMS:
10340 local_player->gems_still_needed = action_arg_number_new;
10343 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10345 DisplayGameControlValues();
10347 DrawGameValue_Emeralds(local_player->gems_still_needed);
10353 #if !USE_PLAYER_GRAVITY
10354 case CA_SET_LEVEL_GRAVITY:
10356 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10357 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10358 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10364 case CA_SET_LEVEL_WIND:
10366 game.wind_direction = action_arg_direction;
10371 /* ---------- player actions ------------------------------------------ */
10373 case CA_MOVE_PLAYER:
10375 /* automatically move to the next field in specified direction */
10376 for (i = 0; i < MAX_PLAYERS; i++)
10377 if (trigger_player_bits & (1 << i))
10378 stored_player[i].programmed_action = action_arg_direction;
10383 case CA_EXIT_PLAYER:
10385 for (i = 0; i < MAX_PLAYERS; i++)
10386 if (action_arg_player_bits & (1 << i))
10387 PlayerWins(&stored_player[i]);
10392 case CA_KILL_PLAYER:
10394 for (i = 0; i < MAX_PLAYERS; i++)
10395 if (action_arg_player_bits & (1 << i))
10396 KillPlayer(&stored_player[i]);
10401 case CA_SET_PLAYER_KEYS:
10403 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10404 int element = getSpecialActionElement(action_arg_element,
10405 action_arg_number, EL_KEY_1);
10407 if (IS_KEY(element))
10409 for (i = 0; i < MAX_PLAYERS; i++)
10411 if (trigger_player_bits & (1 << i))
10413 stored_player[i].key[KEY_NR(element)] = key_state;
10415 DrawGameDoorValues();
10423 case CA_SET_PLAYER_SPEED:
10425 for (i = 0; i < MAX_PLAYERS; i++)
10427 if (trigger_player_bits & (1 << i))
10429 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10431 if (action_arg == CA_ARG_SPEED_FASTER &&
10432 stored_player[i].cannot_move)
10434 action_arg_number = STEPSIZE_VERY_SLOW;
10436 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10437 action_arg == CA_ARG_SPEED_FASTER)
10439 action_arg_number = 2;
10440 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10443 else if (action_arg == CA_ARG_NUMBER_RESET)
10445 action_arg_number = level.initial_player_stepsize[i];
10449 getModifiedActionNumber(move_stepsize,
10452 action_arg_number_min,
10453 action_arg_number_max);
10455 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10462 case CA_SET_PLAYER_SHIELD:
10464 for (i = 0; i < MAX_PLAYERS; i++)
10466 if (trigger_player_bits & (1 << i))
10468 if (action_arg == CA_ARG_SHIELD_OFF)
10470 stored_player[i].shield_normal_time_left = 0;
10471 stored_player[i].shield_deadly_time_left = 0;
10473 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10475 stored_player[i].shield_normal_time_left = 999999;
10477 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10479 stored_player[i].shield_normal_time_left = 999999;
10480 stored_player[i].shield_deadly_time_left = 999999;
10488 #if USE_PLAYER_GRAVITY
10489 case CA_SET_PLAYER_GRAVITY:
10491 for (i = 0; i < MAX_PLAYERS; i++)
10493 if (trigger_player_bits & (1 << i))
10495 stored_player[i].gravity =
10496 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10497 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10498 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10499 stored_player[i].gravity);
10507 case CA_SET_PLAYER_ARTWORK:
10509 for (i = 0; i < MAX_PLAYERS; i++)
10511 if (trigger_player_bits & (1 << i))
10513 int artwork_element = action_arg_element;
10515 if (action_arg == CA_ARG_ELEMENT_RESET)
10517 (level.use_artwork_element[i] ? level.artwork_element[i] :
10518 stored_player[i].element_nr);
10520 #if USE_GFX_RESET_PLAYER_ARTWORK
10521 if (stored_player[i].artwork_element != artwork_element)
10522 stored_player[i].Frame = 0;
10525 stored_player[i].artwork_element = artwork_element;
10527 SetPlayerWaiting(&stored_player[i], FALSE);
10529 /* set number of special actions for bored and sleeping animation */
10530 stored_player[i].num_special_action_bored =
10531 get_num_special_action(artwork_element,
10532 ACTION_BORING_1, ACTION_BORING_LAST);
10533 stored_player[i].num_special_action_sleeping =
10534 get_num_special_action(artwork_element,
10535 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10542 /* ---------- CE actions ---------------------------------------------- */
10544 case CA_SET_CE_VALUE:
10546 #if USE_NEW_CUSTOM_VALUE
10547 int last_ce_value = CustomValue[x][y];
10549 CustomValue[x][y] = action_arg_number_new;
10551 if (CustomValue[x][y] != last_ce_value)
10553 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10554 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10556 if (CustomValue[x][y] == 0)
10558 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10559 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10567 case CA_SET_CE_SCORE:
10569 #if USE_NEW_CUSTOM_VALUE
10570 int last_ce_score = ei->collect_score;
10572 ei->collect_score = action_arg_number_new;
10574 if (ei->collect_score != last_ce_score)
10576 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10577 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10579 if (ei->collect_score == 0)
10583 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10584 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10587 This is a very special case that seems to be a mixture between
10588 CheckElementChange() and CheckTriggeredElementChange(): while
10589 the first one only affects single elements that are triggered
10590 directly, the second one affects multiple elements in the playfield
10591 that are triggered indirectly by another element. This is a third
10592 case: Changing the CE score always affects multiple identical CEs,
10593 so every affected CE must be checked, not only the single CE for
10594 which the CE score was changed in the first place (as every instance
10595 of that CE shares the same CE score, and therefore also can change)!
10597 SCAN_PLAYFIELD(xx, yy)
10599 if (Feld[xx][yy] == element)
10600 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10601 CE_SCORE_GETS_ZERO);
10610 /* ---------- engine actions ------------------------------------------ */
10612 case CA_SET_ENGINE_SCAN_MODE:
10614 InitPlayfieldScanMode(action_arg);
10624 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10626 int old_element = Feld[x][y];
10627 int new_element = GetElementFromGroupElement(element);
10628 int previous_move_direction = MovDir[x][y];
10629 #if USE_NEW_CUSTOM_VALUE
10630 int last_ce_value = CustomValue[x][y];
10632 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10633 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10634 boolean add_player_onto_element = (new_element_is_player &&
10635 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10636 /* this breaks SnakeBite when a snake is
10637 halfway through a door that closes */
10638 /* NOW FIXED AT LEVEL INIT IN files.c */
10639 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10641 IS_WALKABLE(old_element));
10644 /* check if element under the player changes from accessible to unaccessible
10645 (needed for special case of dropping element which then changes) */
10646 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10647 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10655 if (!add_player_onto_element)
10657 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10658 RemoveMovingField(x, y);
10662 Feld[x][y] = new_element;
10664 #if !USE_GFX_RESET_GFX_ANIMATION
10665 ResetGfxAnimation(x, y);
10666 ResetRandomAnimationValue(x, y);
10669 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10670 MovDir[x][y] = previous_move_direction;
10672 #if USE_NEW_CUSTOM_VALUE
10673 if (element_info[new_element].use_last_ce_value)
10674 CustomValue[x][y] = last_ce_value;
10677 InitField_WithBug1(x, y, FALSE);
10679 new_element = Feld[x][y]; /* element may have changed */
10681 #if USE_GFX_RESET_GFX_ANIMATION
10682 ResetGfxAnimation(x, y);
10683 ResetRandomAnimationValue(x, y);
10686 TEST_DrawLevelField(x, y);
10688 if (GFX_CRUMBLED(new_element))
10689 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10693 /* check if element under the player changes from accessible to unaccessible
10694 (needed for special case of dropping element which then changes) */
10695 /* (must be checked after creating new element for walkable group elements) */
10696 #if USE_FIX_KILLED_BY_NON_WALKABLE
10697 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10698 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10705 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10706 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10715 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10716 if (new_element_is_player)
10717 RelocatePlayer(x, y, new_element);
10720 ChangeCount[x][y]++; /* count number of changes in the same frame */
10722 TestIfBadThingTouchesPlayer(x, y);
10723 TestIfPlayerTouchesCustomElement(x, y);
10724 TestIfElementTouchesCustomElement(x, y);
10727 static void CreateField(int x, int y, int element)
10729 CreateFieldExt(x, y, element, FALSE);
10732 static void CreateElementFromChange(int x, int y, int element)
10734 element = GET_VALID_RUNTIME_ELEMENT(element);
10736 #if USE_STOP_CHANGED_ELEMENTS
10737 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10739 int old_element = Feld[x][y];
10741 /* prevent changed element from moving in same engine frame
10742 unless both old and new element can either fall or move */
10743 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10744 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10749 CreateFieldExt(x, y, element, TRUE);
10752 static boolean ChangeElement(int x, int y, int element, int page)
10754 struct ElementInfo *ei = &element_info[element];
10755 struct ElementChangeInfo *change = &ei->change_page[page];
10756 int ce_value = CustomValue[x][y];
10757 int ce_score = ei->collect_score;
10758 int target_element;
10759 int old_element = Feld[x][y];
10761 /* always use default change event to prevent running into a loop */
10762 if (ChangeEvent[x][y] == -1)
10763 ChangeEvent[x][y] = CE_DELAY;
10765 if (ChangeEvent[x][y] == CE_DELAY)
10767 /* reset actual trigger element, trigger player and action element */
10768 change->actual_trigger_element = EL_EMPTY;
10769 change->actual_trigger_player = EL_PLAYER_1;
10770 change->actual_trigger_player_bits = CH_PLAYER_1;
10771 change->actual_trigger_side = CH_SIDE_NONE;
10772 change->actual_trigger_ce_value = 0;
10773 change->actual_trigger_ce_score = 0;
10776 /* do not change elements more than a specified maximum number of changes */
10777 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10780 ChangeCount[x][y]++; /* count number of changes in the same frame */
10782 if (change->explode)
10789 if (change->use_target_content)
10791 boolean complete_replace = TRUE;
10792 boolean can_replace[3][3];
10795 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10798 boolean is_walkable;
10799 boolean is_diggable;
10800 boolean is_collectible;
10801 boolean is_removable;
10802 boolean is_destructible;
10803 int ex = x + xx - 1;
10804 int ey = y + yy - 1;
10805 int content_element = change->target_content.e[xx][yy];
10808 can_replace[xx][yy] = TRUE;
10810 if (ex == x && ey == y) /* do not check changing element itself */
10813 if (content_element == EL_EMPTY_SPACE)
10815 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10820 if (!IN_LEV_FIELD(ex, ey))
10822 can_replace[xx][yy] = FALSE;
10823 complete_replace = FALSE;
10830 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10831 e = MovingOrBlocked2Element(ex, ey);
10833 is_empty = (IS_FREE(ex, ey) ||
10834 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10836 is_walkable = (is_empty || IS_WALKABLE(e));
10837 is_diggable = (is_empty || IS_DIGGABLE(e));
10838 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10839 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10840 is_removable = (is_diggable || is_collectible);
10842 can_replace[xx][yy] =
10843 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10844 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10845 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10846 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10847 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10848 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10849 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10851 if (!can_replace[xx][yy])
10852 complete_replace = FALSE;
10855 if (!change->only_if_complete || complete_replace)
10857 boolean something_has_changed = FALSE;
10859 if (change->only_if_complete && change->use_random_replace &&
10860 RND(100) < change->random_percentage)
10863 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10865 int ex = x + xx - 1;
10866 int ey = y + yy - 1;
10867 int content_element;
10869 if (can_replace[xx][yy] && (!change->use_random_replace ||
10870 RND(100) < change->random_percentage))
10872 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10873 RemoveMovingField(ex, ey);
10875 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10877 content_element = change->target_content.e[xx][yy];
10878 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10879 ce_value, ce_score);
10881 CreateElementFromChange(ex, ey, target_element);
10883 something_has_changed = TRUE;
10885 /* for symmetry reasons, freeze newly created border elements */
10886 if (ex != x || ey != y)
10887 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10891 if (something_has_changed)
10893 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10894 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10900 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10901 ce_value, ce_score);
10903 if (element == EL_DIAGONAL_GROWING ||
10904 element == EL_DIAGONAL_SHRINKING)
10906 target_element = Store[x][y];
10908 Store[x][y] = EL_EMPTY;
10911 CreateElementFromChange(x, y, target_element);
10913 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10914 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10917 /* this uses direct change before indirect change */
10918 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10923 #if USE_NEW_DELAYED_ACTION
10925 static void HandleElementChange(int x, int y, int page)
10927 int element = MovingOrBlocked2Element(x, y);
10928 struct ElementInfo *ei = &element_info[element];
10929 struct ElementChangeInfo *change = &ei->change_page[page];
10932 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10933 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10936 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10937 x, y, element, element_info[element].token_name);
10938 printf("HandleElementChange(): This should never happen!\n");
10943 /* this can happen with classic bombs on walkable, changing elements */
10944 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10947 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10948 ChangeDelay[x][y] = 0;
10954 if (ChangeDelay[x][y] == 0) /* initialize element change */
10956 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10958 if (change->can_change)
10961 /* !!! not clear why graphic animation should be reset at all here !!! */
10962 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10963 #if USE_GFX_RESET_WHEN_NOT_MOVING
10964 /* when a custom element is about to change (for example by change delay),
10965 do not reset graphic animation when the custom element is moving */
10966 if (!IS_MOVING(x, y))
10969 ResetGfxAnimation(x, y);
10970 ResetRandomAnimationValue(x, y);
10974 if (change->pre_change_function)
10975 change->pre_change_function(x, y);
10979 ChangeDelay[x][y]--;
10981 if (ChangeDelay[x][y] != 0) /* continue element change */
10983 if (change->can_change)
10985 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10987 if (IS_ANIMATED(graphic))
10988 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10990 if (change->change_function)
10991 change->change_function(x, y);
10994 else /* finish element change */
10996 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10998 page = ChangePage[x][y];
10999 ChangePage[x][y] = -1;
11001 change = &ei->change_page[page];
11004 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11006 ChangeDelay[x][y] = 1; /* try change after next move step */
11007 ChangePage[x][y] = page; /* remember page to use for change */
11012 if (change->can_change)
11014 if (ChangeElement(x, y, element, page))
11016 if (change->post_change_function)
11017 change->post_change_function(x, y);
11021 if (change->has_action)
11022 ExecuteCustomElementAction(x, y, element, page);
11028 static void HandleElementChange(int x, int y, int page)
11030 int element = MovingOrBlocked2Element(x, y);
11031 struct ElementInfo *ei = &element_info[element];
11032 struct ElementChangeInfo *change = &ei->change_page[page];
11035 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11038 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11039 x, y, element, element_info[element].token_name);
11040 printf("HandleElementChange(): This should never happen!\n");
11045 /* this can happen with classic bombs on walkable, changing elements */
11046 if (!CAN_CHANGE(element))
11049 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11050 ChangeDelay[x][y] = 0;
11056 if (ChangeDelay[x][y] == 0) /* initialize element change */
11058 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11060 ResetGfxAnimation(x, y);
11061 ResetRandomAnimationValue(x, y);
11063 if (change->pre_change_function)
11064 change->pre_change_function(x, y);
11067 ChangeDelay[x][y]--;
11069 if (ChangeDelay[x][y] != 0) /* continue element change */
11071 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11073 if (IS_ANIMATED(graphic))
11074 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11076 if (change->change_function)
11077 change->change_function(x, y);
11079 else /* finish element change */
11081 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11083 page = ChangePage[x][y];
11084 ChangePage[x][y] = -1;
11086 change = &ei->change_page[page];
11089 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11091 ChangeDelay[x][y] = 1; /* try change after next move step */
11092 ChangePage[x][y] = page; /* remember page to use for change */
11097 if (ChangeElement(x, y, element, page))
11099 if (change->post_change_function)
11100 change->post_change_function(x, y);
11107 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11108 int trigger_element,
11110 int trigger_player,
11114 boolean change_done_any = FALSE;
11115 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11118 if (!(trigger_events[trigger_element][trigger_event]))
11122 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11123 trigger_event, recursion_loop_depth, recursion_loop_detected,
11124 recursion_loop_element, EL_NAME(recursion_loop_element));
11127 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11129 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11131 int element = EL_CUSTOM_START + i;
11132 boolean change_done = FALSE;
11135 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11136 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11139 for (p = 0; p < element_info[element].num_change_pages; p++)
11141 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11143 if (change->can_change_or_has_action &&
11144 change->has_event[trigger_event] &&
11145 change->trigger_side & trigger_side &&
11146 change->trigger_player & trigger_player &&
11147 change->trigger_page & trigger_page_bits &&
11148 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11150 change->actual_trigger_element = trigger_element;
11151 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11152 change->actual_trigger_player_bits = trigger_player;
11153 change->actual_trigger_side = trigger_side;
11154 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11155 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11158 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11159 element, EL_NAME(element), p);
11162 if ((change->can_change && !change_done) || change->has_action)
11166 SCAN_PLAYFIELD(x, y)
11168 if (Feld[x][y] == element)
11170 if (change->can_change && !change_done)
11174 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11175 element, EL_NAME(element), p);
11178 ChangeDelay[x][y] = 1;
11179 ChangeEvent[x][y] = trigger_event;
11181 HandleElementChange(x, y, p);
11183 #if USE_NEW_DELAYED_ACTION
11184 else if (change->has_action)
11186 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11187 /* if element already changed in this frame, not only prevent
11188 another element change (checked in ChangeElement()), but
11189 also prevent additional element actions for this element */
11191 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11192 !level.use_action_after_change_bug)
11198 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11199 element, EL_NAME(element), p);
11202 ExecuteCustomElementAction(x, y, element, p);
11203 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11206 if (change->has_action)
11208 ExecuteCustomElementAction(x, y, element, p);
11209 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11215 if (change->can_change)
11217 change_done = TRUE;
11218 change_done_any = TRUE;
11221 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11222 element, EL_NAME(element), p);
11231 RECURSION_LOOP_DETECTION_END();
11233 return change_done_any;
11236 static boolean CheckElementChangeExt(int x, int y,
11238 int trigger_element,
11240 int trigger_player,
11243 boolean change_done = FALSE;
11246 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11247 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11250 if (Feld[x][y] == EL_BLOCKED)
11252 Blocked2Moving(x, y, &x, &y);
11253 element = Feld[x][y];
11257 /* check if element has already changed */
11258 if (Feld[x][y] != element)
11261 /* check if element has already changed or is about to change after moving */
11262 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11263 Feld[x][y] != element) ||
11265 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11266 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11267 ChangePage[x][y] != -1)))
11272 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11273 trigger_event, recursion_loop_depth, recursion_loop_detected,
11274 recursion_loop_element, EL_NAME(recursion_loop_element));
11277 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11279 for (p = 0; p < element_info[element].num_change_pages; p++)
11281 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11283 /* check trigger element for all events where the element that is checked
11284 for changing interacts with a directly adjacent element -- this is
11285 different to element changes that affect other elements to change on the
11286 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11287 boolean check_trigger_element =
11288 (trigger_event == CE_TOUCHING_X ||
11289 trigger_event == CE_HITTING_X ||
11290 trigger_event == CE_HIT_BY_X ||
11292 /* this one was forgotten until 3.2.3 */
11293 trigger_event == CE_DIGGING_X);
11296 if (change->can_change_or_has_action &&
11297 change->has_event[trigger_event] &&
11298 change->trigger_side & trigger_side &&
11299 change->trigger_player & trigger_player &&
11300 (!check_trigger_element ||
11301 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11303 change->actual_trigger_element = trigger_element;
11304 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11305 change->actual_trigger_player_bits = trigger_player;
11306 change->actual_trigger_side = trigger_side;
11307 change->actual_trigger_ce_value = CustomValue[x][y];
11308 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11310 /* special case: trigger element not at (x,y) position for some events */
11311 if (check_trigger_element)
11323 { 0, 0 }, { 0, 0 }, { 0, 0 },
11327 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11328 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11330 change->actual_trigger_ce_value = CustomValue[xx][yy];
11331 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11334 if (change->can_change && !change_done)
11336 ChangeDelay[x][y] = 1;
11337 ChangeEvent[x][y] = trigger_event;
11339 HandleElementChange(x, y, p);
11341 change_done = TRUE;
11343 #if USE_NEW_DELAYED_ACTION
11344 else if (change->has_action)
11346 ExecuteCustomElementAction(x, y, element, p);
11347 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11350 if (change->has_action)
11352 ExecuteCustomElementAction(x, y, element, p);
11353 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11359 RECURSION_LOOP_DETECTION_END();
11361 return change_done;
11364 static void PlayPlayerSound(struct PlayerInfo *player)
11366 int jx = player->jx, jy = player->jy;
11367 int sound_element = player->artwork_element;
11368 int last_action = player->last_action_waiting;
11369 int action = player->action_waiting;
11371 if (player->is_waiting)
11373 if (action != last_action)
11374 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11376 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11380 if (action != last_action)
11381 StopSound(element_info[sound_element].sound[last_action]);
11383 if (last_action == ACTION_SLEEPING)
11384 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11388 static void PlayAllPlayersSound()
11392 for (i = 0; i < MAX_PLAYERS; i++)
11393 if (stored_player[i].active)
11394 PlayPlayerSound(&stored_player[i]);
11397 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11399 boolean last_waiting = player->is_waiting;
11400 int move_dir = player->MovDir;
11402 player->dir_waiting = move_dir;
11403 player->last_action_waiting = player->action_waiting;
11407 if (!last_waiting) /* not waiting -> waiting */
11409 player->is_waiting = TRUE;
11411 player->frame_counter_bored =
11413 game.player_boring_delay_fixed +
11414 GetSimpleRandom(game.player_boring_delay_random);
11415 player->frame_counter_sleeping =
11417 game.player_sleeping_delay_fixed +
11418 GetSimpleRandom(game.player_sleeping_delay_random);
11420 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11423 if (game.player_sleeping_delay_fixed +
11424 game.player_sleeping_delay_random > 0 &&
11425 player->anim_delay_counter == 0 &&
11426 player->post_delay_counter == 0 &&
11427 FrameCounter >= player->frame_counter_sleeping)
11428 player->is_sleeping = TRUE;
11429 else if (game.player_boring_delay_fixed +
11430 game.player_boring_delay_random > 0 &&
11431 FrameCounter >= player->frame_counter_bored)
11432 player->is_bored = TRUE;
11434 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11435 player->is_bored ? ACTION_BORING :
11438 if (player->is_sleeping && player->use_murphy)
11440 /* special case for sleeping Murphy when leaning against non-free tile */
11442 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11443 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11444 !IS_MOVING(player->jx - 1, player->jy)))
11445 move_dir = MV_LEFT;
11446 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11447 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11448 !IS_MOVING(player->jx + 1, player->jy)))
11449 move_dir = MV_RIGHT;
11451 player->is_sleeping = FALSE;
11453 player->dir_waiting = move_dir;
11456 if (player->is_sleeping)
11458 if (player->num_special_action_sleeping > 0)
11460 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11462 int last_special_action = player->special_action_sleeping;
11463 int num_special_action = player->num_special_action_sleeping;
11464 int special_action =
11465 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11466 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11467 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11468 last_special_action + 1 : ACTION_SLEEPING);
11469 int special_graphic =
11470 el_act_dir2img(player->artwork_element, special_action, move_dir);
11472 player->anim_delay_counter =
11473 graphic_info[special_graphic].anim_delay_fixed +
11474 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11475 player->post_delay_counter =
11476 graphic_info[special_graphic].post_delay_fixed +
11477 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11479 player->special_action_sleeping = special_action;
11482 if (player->anim_delay_counter > 0)
11484 player->action_waiting = player->special_action_sleeping;
11485 player->anim_delay_counter--;
11487 else if (player->post_delay_counter > 0)
11489 player->post_delay_counter--;
11493 else if (player->is_bored)
11495 if (player->num_special_action_bored > 0)
11497 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11499 int special_action =
11500 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11501 int special_graphic =
11502 el_act_dir2img(player->artwork_element, special_action, move_dir);
11504 player->anim_delay_counter =
11505 graphic_info[special_graphic].anim_delay_fixed +
11506 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11507 player->post_delay_counter =
11508 graphic_info[special_graphic].post_delay_fixed +
11509 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11511 player->special_action_bored = special_action;
11514 if (player->anim_delay_counter > 0)
11516 player->action_waiting = player->special_action_bored;
11517 player->anim_delay_counter--;
11519 else if (player->post_delay_counter > 0)
11521 player->post_delay_counter--;
11526 else if (last_waiting) /* waiting -> not waiting */
11528 player->is_waiting = FALSE;
11529 player->is_bored = FALSE;
11530 player->is_sleeping = FALSE;
11532 player->frame_counter_bored = -1;
11533 player->frame_counter_sleeping = -1;
11535 player->anim_delay_counter = 0;
11536 player->post_delay_counter = 0;
11538 player->dir_waiting = player->MovDir;
11539 player->action_waiting = ACTION_DEFAULT;
11541 player->special_action_bored = ACTION_DEFAULT;
11542 player->special_action_sleeping = ACTION_DEFAULT;
11546 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11548 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11549 int left = player_action & JOY_LEFT;
11550 int right = player_action & JOY_RIGHT;
11551 int up = player_action & JOY_UP;
11552 int down = player_action & JOY_DOWN;
11553 int button1 = player_action & JOY_BUTTON_1;
11554 int button2 = player_action & JOY_BUTTON_2;
11555 int dx = (left ? -1 : right ? 1 : 0);
11556 int dy = (up ? -1 : down ? 1 : 0);
11558 if (!player->active || tape.pausing)
11564 snapped = SnapField(player, dx, dy);
11568 dropped = DropElement(player);
11570 moved = MovePlayer(player, dx, dy);
11573 if (tape.single_step && tape.recording && !tape.pausing)
11575 if (button1 || (dropped && !moved))
11577 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11578 SnapField(player, 0, 0); /* stop snapping */
11582 SetPlayerWaiting(player, FALSE);
11584 return player_action;
11588 /* no actions for this player (no input at player's configured device) */
11590 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11591 SnapField(player, 0, 0);
11592 CheckGravityMovementWhenNotMoving(player);
11594 if (player->MovPos == 0)
11595 SetPlayerWaiting(player, TRUE);
11597 if (player->MovPos == 0) /* needed for tape.playing */
11598 player->is_moving = FALSE;
11600 player->is_dropping = FALSE;
11601 player->is_dropping_pressed = FALSE;
11602 player->drop_pressed_delay = 0;
11608 static void CheckLevelTime()
11612 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11614 if (level.native_em_level->lev->home == 0) /* all players at home */
11616 PlayerWins(local_player);
11618 AllPlayersGone = TRUE;
11620 level.native_em_level->lev->home = -1;
11623 if (level.native_em_level->ply[0]->alive == 0 &&
11624 level.native_em_level->ply[1]->alive == 0 &&
11625 level.native_em_level->ply[2]->alive == 0 &&
11626 level.native_em_level->ply[3]->alive == 0) /* all dead */
11627 AllPlayersGone = TRUE;
11630 if (TimeFrames >= FRAMES_PER_SECOND)
11635 for (i = 0; i < MAX_PLAYERS; i++)
11637 struct PlayerInfo *player = &stored_player[i];
11639 if (SHIELD_ON(player))
11641 player->shield_normal_time_left--;
11643 if (player->shield_deadly_time_left > 0)
11644 player->shield_deadly_time_left--;
11648 if (!local_player->LevelSolved && !level.use_step_counter)
11656 if (TimeLeft <= 10 && setup.time_limit)
11657 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11660 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11662 DisplayGameControlValues();
11664 DrawGameValue_Time(TimeLeft);
11667 if (!TimeLeft && setup.time_limit)
11669 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11670 level.native_em_level->lev->killed_out_of_time = TRUE;
11672 for (i = 0; i < MAX_PLAYERS; i++)
11673 KillPlayer(&stored_player[i]);
11677 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11679 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11681 DisplayGameControlValues();
11684 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11685 DrawGameValue_Time(TimePlayed);
11688 level.native_em_level->lev->time =
11689 (level.time == 0 ? TimePlayed : TimeLeft);
11692 if (tape.recording || tape.playing)
11693 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11697 UpdateAndDisplayGameControlValues();
11699 UpdateGameDoorValues();
11700 DrawGameDoorValues();
11704 void AdvanceFrameAndPlayerCounters(int player_nr)
11708 /* advance frame counters (global frame counter and time frame counter) */
11712 /* advance player counters (counters for move delay, move animation etc.) */
11713 for (i = 0; i < MAX_PLAYERS; i++)
11715 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11716 int move_delay_value = stored_player[i].move_delay_value;
11717 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11719 if (!advance_player_counters) /* not all players may be affected */
11722 #if USE_NEW_PLAYER_ANIM
11723 if (move_frames == 0) /* less than one move per game frame */
11725 int stepsize = TILEX / move_delay_value;
11726 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11727 int count = (stored_player[i].is_moving ?
11728 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11730 if (count % delay == 0)
11735 stored_player[i].Frame += move_frames;
11737 if (stored_player[i].MovPos != 0)
11738 stored_player[i].StepFrame += move_frames;
11740 if (stored_player[i].move_delay > 0)
11741 stored_player[i].move_delay--;
11743 /* due to bugs in previous versions, counter must count up, not down */
11744 if (stored_player[i].push_delay != -1)
11745 stored_player[i].push_delay++;
11747 if (stored_player[i].drop_delay > 0)
11748 stored_player[i].drop_delay--;
11750 if (stored_player[i].is_dropping_pressed)
11751 stored_player[i].drop_pressed_delay++;
11755 void StartGameActions(boolean init_network_game, boolean record_tape,
11758 unsigned long new_random_seed = InitRND(random_seed);
11761 TapeStartRecording(new_random_seed);
11763 #if defined(NETWORK_AVALIABLE)
11764 if (init_network_game)
11766 SendToServer_StartPlaying();
11777 static unsigned long game_frame_delay = 0;
11778 unsigned long game_frame_delay_value;
11779 byte *recorded_player_action;
11780 byte summarized_player_action = 0;
11781 byte tape_action[MAX_PLAYERS];
11784 /* detect endless loops, caused by custom element programming */
11785 if (recursion_loop_detected && recursion_loop_depth == 0)
11787 char *message = getStringCat3("Internal Error ! Element ",
11788 EL_NAME(recursion_loop_element),
11789 " caused endless loop ! Quit the game ?");
11791 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11792 EL_NAME(recursion_loop_element));
11794 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11796 recursion_loop_detected = FALSE; /* if game should be continued */
11803 if (game.restart_level)
11804 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11806 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11808 if (level.native_em_level->lev->home == 0) /* all players at home */
11810 PlayerWins(local_player);
11812 AllPlayersGone = TRUE;
11814 level.native_em_level->lev->home = -1;
11817 if (level.native_em_level->ply[0]->alive == 0 &&
11818 level.native_em_level->ply[1]->alive == 0 &&
11819 level.native_em_level->ply[2]->alive == 0 &&
11820 level.native_em_level->ply[3]->alive == 0) /* all dead */
11821 AllPlayersGone = TRUE;
11824 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11827 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11830 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11833 game_frame_delay_value =
11834 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11836 if (tape.playing && tape.warp_forward && !tape.pausing)
11837 game_frame_delay_value = 0;
11839 /* ---------- main game synchronization point ---------- */
11841 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11843 if (network_playing && !network_player_action_received)
11845 /* try to get network player actions in time */
11847 #if defined(NETWORK_AVALIABLE)
11848 /* last chance to get network player actions without main loop delay */
11849 HandleNetworking();
11852 /* game was quit by network peer */
11853 if (game_status != GAME_MODE_PLAYING)
11856 if (!network_player_action_received)
11857 return; /* failed to get network player actions in time */
11859 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11865 /* at this point we know that we really continue executing the game */
11867 network_player_action_received = FALSE;
11869 /* when playing tape, read previously recorded player input from tape data */
11870 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11873 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11878 if (tape.set_centered_player)
11880 game.centered_player_nr_next = tape.centered_player_nr_next;
11881 game.set_centered_player = TRUE;
11884 for (i = 0; i < MAX_PLAYERS; i++)
11886 summarized_player_action |= stored_player[i].action;
11888 if (!network_playing)
11889 stored_player[i].effective_action = stored_player[i].action;
11892 #if defined(NETWORK_AVALIABLE)
11893 if (network_playing)
11894 SendToServer_MovePlayer(summarized_player_action);
11897 if (!options.network && !setup.team_mode)
11898 local_player->effective_action = summarized_player_action;
11900 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11902 for (i = 0; i < MAX_PLAYERS; i++)
11903 stored_player[i].effective_action =
11904 (i == game.centered_player_nr ? summarized_player_action : 0);
11907 if (recorded_player_action != NULL)
11908 for (i = 0; i < MAX_PLAYERS; i++)
11909 stored_player[i].effective_action = recorded_player_action[i];
11911 for (i = 0; i < MAX_PLAYERS; i++)
11913 tape_action[i] = stored_player[i].effective_action;
11915 /* (this can only happen in the R'n'D game engine) */
11916 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11917 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11920 /* only record actions from input devices, but not programmed actions */
11921 if (tape.recording)
11922 TapeRecordAction(tape_action);
11924 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11926 GameActions_EM_Main();
11934 void GameActions_EM_Main()
11936 byte effective_action[MAX_PLAYERS];
11937 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11940 for (i = 0; i < MAX_PLAYERS; i++)
11941 effective_action[i] = stored_player[i].effective_action;
11943 GameActions_EM(effective_action, warp_mode);
11947 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11950 void GameActions_RND()
11952 int magic_wall_x = 0, magic_wall_y = 0;
11953 int i, x, y, element, graphic;
11955 InitPlayfieldScanModeVars();
11957 #if USE_ONE_MORE_CHANGE_PER_FRAME
11958 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11960 SCAN_PLAYFIELD(x, y)
11962 ChangeCount[x][y] = 0;
11963 ChangeEvent[x][y] = -1;
11968 if (game.set_centered_player)
11970 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11972 /* switching to "all players" only possible if all players fit to screen */
11973 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11975 game.centered_player_nr_next = game.centered_player_nr;
11976 game.set_centered_player = FALSE;
11979 /* do not switch focus to non-existing (or non-active) player */
11980 if (game.centered_player_nr_next >= 0 &&
11981 !stored_player[game.centered_player_nr_next].active)
11983 game.centered_player_nr_next = game.centered_player_nr;
11984 game.set_centered_player = FALSE;
11988 if (game.set_centered_player &&
11989 ScreenMovPos == 0) /* screen currently aligned at tile position */
11993 if (game.centered_player_nr_next == -1)
11995 setScreenCenteredToAllPlayers(&sx, &sy);
11999 sx = stored_player[game.centered_player_nr_next].jx;
12000 sy = stored_player[game.centered_player_nr_next].jy;
12003 game.centered_player_nr = game.centered_player_nr_next;
12004 game.set_centered_player = FALSE;
12006 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12007 DrawGameDoorValues();
12010 for (i = 0; i < MAX_PLAYERS; i++)
12012 int actual_player_action = stored_player[i].effective_action;
12015 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12016 - rnd_equinox_tetrachloride 048
12017 - rnd_equinox_tetrachloride_ii 096
12018 - rnd_emanuel_schmieg 002
12019 - doctor_sloan_ww 001, 020
12021 if (stored_player[i].MovPos == 0)
12022 CheckGravityMovement(&stored_player[i]);
12025 /* overwrite programmed action with tape action */
12026 if (stored_player[i].programmed_action)
12027 actual_player_action = stored_player[i].programmed_action;
12029 PlayerActions(&stored_player[i], actual_player_action);
12031 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12034 ScrollScreen(NULL, SCROLL_GO_ON);
12036 /* for backwards compatibility, the following code emulates a fixed bug that
12037 occured when pushing elements (causing elements that just made their last
12038 pushing step to already (if possible) make their first falling step in the
12039 same game frame, which is bad); this code is also needed to use the famous
12040 "spring push bug" which is used in older levels and might be wanted to be
12041 used also in newer levels, but in this case the buggy pushing code is only
12042 affecting the "spring" element and no other elements */
12044 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12046 for (i = 0; i < MAX_PLAYERS; i++)
12048 struct PlayerInfo *player = &stored_player[i];
12049 int x = player->jx;
12050 int y = player->jy;
12052 if (player->active && player->is_pushing && player->is_moving &&
12054 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12055 Feld[x][y] == EL_SPRING))
12057 ContinueMoving(x, y);
12059 /* continue moving after pushing (this is actually a bug) */
12060 if (!IS_MOVING(x, y))
12061 Stop[x][y] = FALSE;
12067 debug_print_timestamp(0, "start main loop profiling");
12070 SCAN_PLAYFIELD(x, y)
12072 ChangeCount[x][y] = 0;
12073 ChangeEvent[x][y] = -1;
12075 /* this must be handled before main playfield loop */
12076 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12079 if (MovDelay[x][y] <= 0)
12083 #if USE_NEW_SNAP_DELAY
12084 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12087 if (MovDelay[x][y] <= 0)
12090 TEST_DrawLevelField(x, y);
12092 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12098 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12100 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12101 printf("GameActions(): This should never happen!\n");
12103 ChangePage[x][y] = -1;
12107 Stop[x][y] = FALSE;
12108 if (WasJustMoving[x][y] > 0)
12109 WasJustMoving[x][y]--;
12110 if (WasJustFalling[x][y] > 0)
12111 WasJustFalling[x][y]--;
12112 if (CheckCollision[x][y] > 0)
12113 CheckCollision[x][y]--;
12114 if (CheckImpact[x][y] > 0)
12115 CheckImpact[x][y]--;
12119 /* reset finished pushing action (not done in ContinueMoving() to allow
12120 continuous pushing animation for elements with zero push delay) */
12121 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12123 ResetGfxAnimation(x, y);
12124 TEST_DrawLevelField(x, y);
12128 if (IS_BLOCKED(x, y))
12132 Blocked2Moving(x, y, &oldx, &oldy);
12133 if (!IS_MOVING(oldx, oldy))
12135 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12136 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12137 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12138 printf("GameActions(): This should never happen!\n");
12145 debug_print_timestamp(0, "- time for pre-main loop:");
12148 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12149 SCAN_PLAYFIELD(x, y)
12151 element = Feld[x][y];
12152 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12157 int element2 = element;
12158 int graphic2 = graphic;
12160 int element2 = Feld[x][y];
12161 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12163 int last_gfx_frame = GfxFrame[x][y];
12165 if (graphic_info[graphic2].anim_global_sync)
12166 GfxFrame[x][y] = FrameCounter;
12167 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12168 GfxFrame[x][y] = CustomValue[x][y];
12169 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12170 GfxFrame[x][y] = element_info[element2].collect_score;
12171 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12172 GfxFrame[x][y] = ChangeDelay[x][y];
12174 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12175 DrawLevelGraphicAnimation(x, y, graphic2);
12178 ResetGfxFrame(x, y, TRUE);
12182 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12183 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12184 ResetRandomAnimationValue(x, y);
12188 SetRandomAnimationValue(x, y);
12192 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12195 #endif // -------------------- !!! TEST ONLY !!! --------------------
12198 debug_print_timestamp(0, "- time for TEST loop: -->");
12201 SCAN_PLAYFIELD(x, y)
12203 element = Feld[x][y];
12204 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12206 ResetGfxFrame(x, y, TRUE);
12208 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12209 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12210 ResetRandomAnimationValue(x, y);
12212 SetRandomAnimationValue(x, y);
12214 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12216 if (IS_INACTIVE(element))
12218 if (IS_ANIMATED(graphic))
12219 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12224 /* this may take place after moving, so 'element' may have changed */
12225 if (IS_CHANGING(x, y) &&
12226 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12228 int page = element_info[element].event_page_nr[CE_DELAY];
12231 HandleElementChange(x, y, page);
12233 if (CAN_CHANGE(element))
12234 HandleElementChange(x, y, page);
12236 if (HAS_ACTION(element))
12237 ExecuteCustomElementAction(x, y, element, page);
12240 element = Feld[x][y];
12241 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12244 #if 0 // ---------------------------------------------------------------------
12246 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12250 element = Feld[x][y];
12251 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12253 if (IS_ANIMATED(graphic) &&
12254 !IS_MOVING(x, y) &&
12256 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12258 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12259 TEST_DrawTwinkleOnField(x, y);
12261 else if (IS_MOVING(x, y))
12262 ContinueMoving(x, y);
12269 case EL_EM_EXIT_OPEN:
12270 case EL_SP_EXIT_OPEN:
12271 case EL_STEEL_EXIT_OPEN:
12272 case EL_EM_STEEL_EXIT_OPEN:
12273 case EL_SP_TERMINAL:
12274 case EL_SP_TERMINAL_ACTIVE:
12275 case EL_EXTRA_TIME:
12276 case EL_SHIELD_NORMAL:
12277 case EL_SHIELD_DEADLY:
12278 if (IS_ANIMATED(graphic))
12279 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12282 case EL_DYNAMITE_ACTIVE:
12283 case EL_EM_DYNAMITE_ACTIVE:
12284 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12285 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12286 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12287 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12288 case EL_SP_DISK_RED_ACTIVE:
12289 CheckDynamite(x, y);
12292 case EL_AMOEBA_GROWING:
12293 AmoebeWaechst(x, y);
12296 case EL_AMOEBA_SHRINKING:
12297 AmoebaDisappearing(x, y);
12300 #if !USE_NEW_AMOEBA_CODE
12301 case EL_AMOEBA_WET:
12302 case EL_AMOEBA_DRY:
12303 case EL_AMOEBA_FULL:
12305 case EL_EMC_DRIPPER:
12306 AmoebeAbleger(x, y);
12310 case EL_GAME_OF_LIFE:
12315 case EL_EXIT_CLOSED:
12319 case EL_EM_EXIT_CLOSED:
12323 case EL_STEEL_EXIT_CLOSED:
12324 CheckExitSteel(x, y);
12327 case EL_EM_STEEL_EXIT_CLOSED:
12328 CheckExitSteelEM(x, y);
12331 case EL_SP_EXIT_CLOSED:
12335 case EL_EXPANDABLE_WALL_GROWING:
12336 case EL_EXPANDABLE_STEELWALL_GROWING:
12337 MauerWaechst(x, y);
12340 case EL_EXPANDABLE_WALL:
12341 case EL_EXPANDABLE_WALL_HORIZONTAL:
12342 case EL_EXPANDABLE_WALL_VERTICAL:
12343 case EL_EXPANDABLE_WALL_ANY:
12344 case EL_BD_EXPANDABLE_WALL:
12345 MauerAbleger(x, y);
12348 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12349 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12350 case EL_EXPANDABLE_STEELWALL_ANY:
12351 MauerAblegerStahl(x, y);
12355 CheckForDragon(x, y);
12361 case EL_ELEMENT_SNAPPING:
12362 case EL_DIAGONAL_SHRINKING:
12363 case EL_DIAGONAL_GROWING:
12366 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12368 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12373 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12374 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12379 #else // ---------------------------------------------------------------------
12381 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12385 element = Feld[x][y];
12386 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12388 if (IS_ANIMATED(graphic) &&
12389 !IS_MOVING(x, y) &&
12391 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12393 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12394 TEST_DrawTwinkleOnField(x, y);
12396 else if ((element == EL_ACID ||
12397 element == EL_EXIT_OPEN ||
12398 element == EL_EM_EXIT_OPEN ||
12399 element == EL_SP_EXIT_OPEN ||
12400 element == EL_STEEL_EXIT_OPEN ||
12401 element == EL_EM_STEEL_EXIT_OPEN ||
12402 element == EL_SP_TERMINAL ||
12403 element == EL_SP_TERMINAL_ACTIVE ||
12404 element == EL_EXTRA_TIME ||
12405 element == EL_SHIELD_NORMAL ||
12406 element == EL_SHIELD_DEADLY) &&
12407 IS_ANIMATED(graphic))
12408 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12409 else if (IS_MOVING(x, y))
12410 ContinueMoving(x, y);
12411 else if (IS_ACTIVE_BOMB(element))
12412 CheckDynamite(x, y);
12413 else if (element == EL_AMOEBA_GROWING)
12414 AmoebeWaechst(x, y);
12415 else if (element == EL_AMOEBA_SHRINKING)
12416 AmoebaDisappearing(x, y);
12418 #if !USE_NEW_AMOEBA_CODE
12419 else if (IS_AMOEBALIVE(element))
12420 AmoebeAbleger(x, y);
12423 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12425 else if (element == EL_EXIT_CLOSED)
12427 else if (element == EL_EM_EXIT_CLOSED)
12429 else if (element == EL_STEEL_EXIT_CLOSED)
12430 CheckExitSteel(x, y);
12431 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12432 CheckExitSteelEM(x, y);
12433 else if (element == EL_SP_EXIT_CLOSED)
12435 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12436 element == EL_EXPANDABLE_STEELWALL_GROWING)
12437 MauerWaechst(x, y);
12438 else if (element == EL_EXPANDABLE_WALL ||
12439 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12440 element == EL_EXPANDABLE_WALL_VERTICAL ||
12441 element == EL_EXPANDABLE_WALL_ANY ||
12442 element == EL_BD_EXPANDABLE_WALL)
12443 MauerAbleger(x, y);
12444 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12445 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12446 element == EL_EXPANDABLE_STEELWALL_ANY)
12447 MauerAblegerStahl(x, y);
12448 else if (element == EL_FLAMES)
12449 CheckForDragon(x, y);
12450 else if (element == EL_EXPLOSION)
12451 ; /* drawing of correct explosion animation is handled separately */
12452 else if (element == EL_ELEMENT_SNAPPING ||
12453 element == EL_DIAGONAL_SHRINKING ||
12454 element == EL_DIAGONAL_GROWING)
12456 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12458 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12460 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12461 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12463 #endif // ---------------------------------------------------------------------
12465 if (IS_BELT_ACTIVE(element))
12466 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12468 if (game.magic_wall_active)
12470 int jx = local_player->jx, jy = local_player->jy;
12472 /* play the element sound at the position nearest to the player */
12473 if ((element == EL_MAGIC_WALL_FULL ||
12474 element == EL_MAGIC_WALL_ACTIVE ||
12475 element == EL_MAGIC_WALL_EMPTYING ||
12476 element == EL_BD_MAGIC_WALL_FULL ||
12477 element == EL_BD_MAGIC_WALL_ACTIVE ||
12478 element == EL_BD_MAGIC_WALL_EMPTYING ||
12479 element == EL_DC_MAGIC_WALL_FULL ||
12480 element == EL_DC_MAGIC_WALL_ACTIVE ||
12481 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12482 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12491 debug_print_timestamp(0, "- time for MAIN loop: -->");
12494 #if USE_NEW_AMOEBA_CODE
12495 /* new experimental amoeba growth stuff */
12496 if (!(FrameCounter % 8))
12498 static unsigned long random = 1684108901;
12500 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12502 x = RND(lev_fieldx);
12503 y = RND(lev_fieldy);
12504 element = Feld[x][y];
12506 if (!IS_PLAYER(x,y) &&
12507 (element == EL_EMPTY ||
12508 CAN_GROW_INTO(element) ||
12509 element == EL_QUICKSAND_EMPTY ||
12510 element == EL_QUICKSAND_FAST_EMPTY ||
12511 element == EL_ACID_SPLASH_LEFT ||
12512 element == EL_ACID_SPLASH_RIGHT))
12514 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12515 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12516 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12517 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12518 Feld[x][y] = EL_AMOEBA_DROP;
12521 random = random * 129 + 1;
12527 if (game.explosions_delayed)
12530 game.explosions_delayed = FALSE;
12532 SCAN_PLAYFIELD(x, y)
12534 element = Feld[x][y];
12536 if (ExplodeField[x][y])
12537 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12538 else if (element == EL_EXPLOSION)
12539 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12541 ExplodeField[x][y] = EX_TYPE_NONE;
12544 game.explosions_delayed = TRUE;
12547 if (game.magic_wall_active)
12549 if (!(game.magic_wall_time_left % 4))
12551 int element = Feld[magic_wall_x][magic_wall_y];
12553 if (element == EL_BD_MAGIC_WALL_FULL ||
12554 element == EL_BD_MAGIC_WALL_ACTIVE ||
12555 element == EL_BD_MAGIC_WALL_EMPTYING)
12556 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12557 else if (element == EL_DC_MAGIC_WALL_FULL ||
12558 element == EL_DC_MAGIC_WALL_ACTIVE ||
12559 element == EL_DC_MAGIC_WALL_EMPTYING)
12560 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12562 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12565 if (game.magic_wall_time_left > 0)
12567 game.magic_wall_time_left--;
12569 if (!game.magic_wall_time_left)
12571 SCAN_PLAYFIELD(x, y)
12573 element = Feld[x][y];
12575 if (element == EL_MAGIC_WALL_ACTIVE ||
12576 element == EL_MAGIC_WALL_FULL)
12578 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12579 TEST_DrawLevelField(x, y);
12581 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12582 element == EL_BD_MAGIC_WALL_FULL)
12584 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12585 TEST_DrawLevelField(x, y);
12587 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12588 element == EL_DC_MAGIC_WALL_FULL)
12590 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12591 TEST_DrawLevelField(x, y);
12595 game.magic_wall_active = FALSE;
12600 if (game.light_time_left > 0)
12602 game.light_time_left--;
12604 if (game.light_time_left == 0)
12605 RedrawAllLightSwitchesAndInvisibleElements();
12608 if (game.timegate_time_left > 0)
12610 game.timegate_time_left--;
12612 if (game.timegate_time_left == 0)
12613 CloseAllOpenTimegates();
12616 if (game.lenses_time_left > 0)
12618 game.lenses_time_left--;
12620 if (game.lenses_time_left == 0)
12621 RedrawAllInvisibleElementsForLenses();
12624 if (game.magnify_time_left > 0)
12626 game.magnify_time_left--;
12628 if (game.magnify_time_left == 0)
12629 RedrawAllInvisibleElementsForMagnifier();
12632 for (i = 0; i < MAX_PLAYERS; i++)
12634 struct PlayerInfo *player = &stored_player[i];
12636 if (SHIELD_ON(player))
12638 if (player->shield_deadly_time_left)
12639 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12640 else if (player->shield_normal_time_left)
12641 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12645 #if USE_DELAYED_GFX_REDRAW
12646 SCAN_PLAYFIELD(x, y)
12649 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12651 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12652 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12655 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12656 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12658 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12659 DrawLevelField(x, y);
12661 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12662 DrawLevelFieldCrumbledSand(x, y);
12664 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12665 DrawLevelFieldCrumbledSandNeighbours(x, y);
12667 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12668 DrawTwinkleOnField(x, y);
12671 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12678 PlayAllPlayersSound();
12680 if (options.debug) /* calculate frames per second */
12682 static unsigned long fps_counter = 0;
12683 static int fps_frames = 0;
12684 unsigned long fps_delay_ms = Counter() - fps_counter;
12688 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12690 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12693 fps_counter = Counter();
12696 redraw_mask |= REDRAW_FPS;
12699 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12701 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12703 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12705 local_player->show_envelope = 0;
12709 debug_print_timestamp(0, "stop main loop profiling ");
12710 printf("----------------------------------------------------------\n");
12713 /* use random number generator in every frame to make it less predictable */
12714 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12718 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12720 int min_x = x, min_y = y, max_x = x, max_y = y;
12723 for (i = 0; i < MAX_PLAYERS; i++)
12725 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12727 if (!stored_player[i].active || &stored_player[i] == player)
12730 min_x = MIN(min_x, jx);
12731 min_y = MIN(min_y, jy);
12732 max_x = MAX(max_x, jx);
12733 max_y = MAX(max_y, jy);
12736 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12739 static boolean AllPlayersInVisibleScreen()
12743 for (i = 0; i < MAX_PLAYERS; i++)
12745 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12747 if (!stored_player[i].active)
12750 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12757 void ScrollLevel(int dx, int dy)
12760 /* (directly solved in BlitBitmap() now) */
12761 static Bitmap *bitmap_db_field2 = NULL;
12762 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12769 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12770 /* only horizontal XOR vertical scroll direction allowed */
12771 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12776 /* (directly solved in BlitBitmap() now) */
12777 if (bitmap_db_field2 == NULL)
12778 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12780 /* needed when blitting directly to same bitmap -- should not be needed with
12781 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12782 BlitBitmap(drawto_field, bitmap_db_field2,
12783 FX + TILEX * (dx == -1) - softscroll_offset,
12784 FY + TILEY * (dy == -1) - softscroll_offset,
12785 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12786 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12787 FX + TILEX * (dx == 1) - softscroll_offset,
12788 FY + TILEY * (dy == 1) - softscroll_offset);
12789 BlitBitmap(bitmap_db_field2, drawto_field,
12790 FX + TILEX * (dx == 1) - softscroll_offset,
12791 FY + TILEY * (dy == 1) - softscroll_offset,
12792 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12793 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12794 FX + TILEX * (dx == 1) - softscroll_offset,
12795 FY + TILEY * (dy == 1) - softscroll_offset);
12800 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12801 int xsize = (BX2 - BX1 + 1);
12802 int ysize = (BY2 - BY1 + 1);
12803 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12804 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12805 int step = (start < end ? +1 : -1);
12807 for (i = start; i != end; i += step)
12809 BlitBitmap(drawto_field, drawto_field,
12810 FX + TILEX * (dx != 0 ? i + step : 0),
12811 FY + TILEY * (dy != 0 ? i + step : 0),
12812 TILEX * (dx != 0 ? 1 : xsize),
12813 TILEY * (dy != 0 ? 1 : ysize),
12814 FX + TILEX * (dx != 0 ? i : 0),
12815 FY + TILEY * (dy != 0 ? i : 0));
12820 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12822 BlitBitmap(drawto_field, drawto_field,
12823 FX + TILEX * (dx == -1) - softscroll_offset,
12824 FY + TILEY * (dy == -1) - softscroll_offset,
12825 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12826 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12827 FX + TILEX * (dx == 1) - softscroll_offset,
12828 FY + TILEY * (dy == 1) - softscroll_offset);
12834 x = (dx == 1 ? BX1 : BX2);
12835 for (y = BY1; y <= BY2; y++)
12836 DrawScreenField(x, y);
12841 y = (dy == 1 ? BY1 : BY2);
12842 for (x = BX1; x <= BX2; x++)
12843 DrawScreenField(x, y);
12846 redraw_mask |= REDRAW_FIELD;
12849 static boolean canFallDown(struct PlayerInfo *player)
12851 int jx = player->jx, jy = player->jy;
12853 return (IN_LEV_FIELD(jx, jy + 1) &&
12854 (IS_FREE(jx, jy + 1) ||
12855 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12856 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12857 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12860 static boolean canPassField(int x, int y, int move_dir)
12862 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12863 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12864 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12865 int nextx = x + dx;
12866 int nexty = y + dy;
12867 int element = Feld[x][y];
12869 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12870 !CAN_MOVE(element) &&
12871 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12872 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12873 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12876 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12878 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12879 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12880 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12884 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12885 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12886 (IS_DIGGABLE(Feld[newx][newy]) ||
12887 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12888 canPassField(newx, newy, move_dir)));
12891 static void CheckGravityMovement(struct PlayerInfo *player)
12893 #if USE_PLAYER_GRAVITY
12894 if (player->gravity && !player->programmed_action)
12896 if (game.gravity && !player->programmed_action)
12899 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12900 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12901 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12902 int jx = player->jx, jy = player->jy;
12903 boolean player_is_moving_to_valid_field =
12904 (!player_is_snapping &&
12905 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12906 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12907 boolean player_can_fall_down = canFallDown(player);
12909 if (player_can_fall_down &&
12910 !player_is_moving_to_valid_field)
12911 player->programmed_action = MV_DOWN;
12915 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12917 return CheckGravityMovement(player);
12919 #if USE_PLAYER_GRAVITY
12920 if (player->gravity && !player->programmed_action)
12922 if (game.gravity && !player->programmed_action)
12925 int jx = player->jx, jy = player->jy;
12926 boolean field_under_player_is_free =
12927 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12928 boolean player_is_standing_on_valid_field =
12929 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12930 (IS_WALKABLE(Feld[jx][jy]) &&
12931 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12933 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12934 player->programmed_action = MV_DOWN;
12939 MovePlayerOneStep()
12940 -----------------------------------------------------------------------------
12941 dx, dy: direction (non-diagonal) to try to move the player to
12942 real_dx, real_dy: direction as read from input device (can be diagonal)
12945 boolean MovePlayerOneStep(struct PlayerInfo *player,
12946 int dx, int dy, int real_dx, int real_dy)
12948 int jx = player->jx, jy = player->jy;
12949 int new_jx = jx + dx, new_jy = jy + dy;
12950 #if !USE_FIXED_DONT_RUN_INTO
12954 boolean player_can_move = !player->cannot_move;
12956 if (!player->active || (!dx && !dy))
12957 return MP_NO_ACTION;
12959 player->MovDir = (dx < 0 ? MV_LEFT :
12960 dx > 0 ? MV_RIGHT :
12962 dy > 0 ? MV_DOWN : MV_NONE);
12964 if (!IN_LEV_FIELD(new_jx, new_jy))
12965 return MP_NO_ACTION;
12967 if (!player_can_move)
12969 if (player->MovPos == 0)
12971 player->is_moving = FALSE;
12972 player->is_digging = FALSE;
12973 player->is_collecting = FALSE;
12974 player->is_snapping = FALSE;
12975 player->is_pushing = FALSE;
12980 if (!options.network && game.centered_player_nr == -1 &&
12981 !AllPlayersInSight(player, new_jx, new_jy))
12982 return MP_NO_ACTION;
12984 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12985 return MP_NO_ACTION;
12988 #if !USE_FIXED_DONT_RUN_INTO
12989 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12991 /* (moved to DigField()) */
12992 if (player_can_move && DONT_RUN_INTO(element))
12994 if (element == EL_ACID && dx == 0 && dy == 1)
12996 SplashAcid(new_jx, new_jy);
12997 Feld[jx][jy] = EL_PLAYER_1;
12998 InitMovingField(jx, jy, MV_DOWN);
12999 Store[jx][jy] = EL_ACID;
13000 ContinueMoving(jx, jy);
13001 BuryPlayer(player);
13004 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13010 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13011 if (can_move != MP_MOVING)
13014 /* check if DigField() has caused relocation of the player */
13015 if (player->jx != jx || player->jy != jy)
13016 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13018 StorePlayer[jx][jy] = 0;
13019 player->last_jx = jx;
13020 player->last_jy = jy;
13021 player->jx = new_jx;
13022 player->jy = new_jy;
13023 StorePlayer[new_jx][new_jy] = player->element_nr;
13025 if (player->move_delay_value_next != -1)
13027 player->move_delay_value = player->move_delay_value_next;
13028 player->move_delay_value_next = -1;
13032 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13034 player->step_counter++;
13036 PlayerVisit[jx][jy] = FrameCounter;
13038 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13039 player->is_moving = TRUE;
13043 /* should better be called in MovePlayer(), but this breaks some tapes */
13044 ScrollPlayer(player, SCROLL_INIT);
13050 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13052 int jx = player->jx, jy = player->jy;
13053 int old_jx = jx, old_jy = jy;
13054 int moved = MP_NO_ACTION;
13056 if (!player->active)
13061 if (player->MovPos == 0)
13063 player->is_moving = FALSE;
13064 player->is_digging = FALSE;
13065 player->is_collecting = FALSE;
13066 player->is_snapping = FALSE;
13067 player->is_pushing = FALSE;
13073 if (player->move_delay > 0)
13076 player->move_delay = -1; /* set to "uninitialized" value */
13078 /* store if player is automatically moved to next field */
13079 player->is_auto_moving = (player->programmed_action != MV_NONE);
13081 /* remove the last programmed player action */
13082 player->programmed_action = 0;
13084 if (player->MovPos)
13086 /* should only happen if pre-1.2 tape recordings are played */
13087 /* this is only for backward compatibility */
13089 int original_move_delay_value = player->move_delay_value;
13092 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13096 /* scroll remaining steps with finest movement resolution */
13097 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13099 while (player->MovPos)
13101 ScrollPlayer(player, SCROLL_GO_ON);
13102 ScrollScreen(NULL, SCROLL_GO_ON);
13104 AdvanceFrameAndPlayerCounters(player->index_nr);
13110 player->move_delay_value = original_move_delay_value;
13113 player->is_active = FALSE;
13115 if (player->last_move_dir & MV_HORIZONTAL)
13117 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13118 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13122 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13123 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13126 #if USE_FIXED_BORDER_RUNNING_GFX
13127 if (!moved && !player->is_active)
13129 player->is_moving = FALSE;
13130 player->is_digging = FALSE;
13131 player->is_collecting = FALSE;
13132 player->is_snapping = FALSE;
13133 player->is_pushing = FALSE;
13141 if (moved & MP_MOVING && !ScreenMovPos &&
13142 (player->index_nr == game.centered_player_nr ||
13143 game.centered_player_nr == -1))
13145 if (moved & MP_MOVING && !ScreenMovPos &&
13146 (player == local_player || !options.network))
13149 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13150 int offset = game.scroll_delay_value;
13152 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13154 /* actual player has left the screen -- scroll in that direction */
13155 if (jx != old_jx) /* player has moved horizontally */
13156 scroll_x += (jx - old_jx);
13157 else /* player has moved vertically */
13158 scroll_y += (jy - old_jy);
13162 if (jx != old_jx) /* player has moved horizontally */
13164 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13165 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13166 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13168 /* don't scroll over playfield boundaries */
13169 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13170 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13172 /* don't scroll more than one field at a time */
13173 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13175 /* don't scroll against the player's moving direction */
13176 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13177 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13178 scroll_x = old_scroll_x;
13180 else /* player has moved vertically */
13182 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13183 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13184 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13186 /* don't scroll over playfield boundaries */
13187 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13188 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13190 /* don't scroll more than one field at a time */
13191 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13193 /* don't scroll against the player's moving direction */
13194 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13195 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13196 scroll_y = old_scroll_y;
13200 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13203 if (!options.network && game.centered_player_nr == -1 &&
13204 !AllPlayersInVisibleScreen())
13206 scroll_x = old_scroll_x;
13207 scroll_y = old_scroll_y;
13211 if (!options.network && !AllPlayersInVisibleScreen())
13213 scroll_x = old_scroll_x;
13214 scroll_y = old_scroll_y;
13219 ScrollScreen(player, SCROLL_INIT);
13220 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13225 player->StepFrame = 0;
13227 if (moved & MP_MOVING)
13229 if (old_jx != jx && old_jy == jy)
13230 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13231 else if (old_jx == jx && old_jy != jy)
13232 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13234 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13236 player->last_move_dir = player->MovDir;
13237 player->is_moving = TRUE;
13238 player->is_snapping = FALSE;
13239 player->is_switching = FALSE;
13240 player->is_dropping = FALSE;
13241 player->is_dropping_pressed = FALSE;
13242 player->drop_pressed_delay = 0;
13245 /* should better be called here than above, but this breaks some tapes */
13246 ScrollPlayer(player, SCROLL_INIT);
13251 CheckGravityMovementWhenNotMoving(player);
13253 player->is_moving = FALSE;
13255 /* at this point, the player is allowed to move, but cannot move right now
13256 (e.g. because of something blocking the way) -- ensure that the player
13257 is also allowed to move in the next frame (in old versions before 3.1.1,
13258 the player was forced to wait again for eight frames before next try) */
13260 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13261 player->move_delay = 0; /* allow direct movement in the next frame */
13264 if (player->move_delay == -1) /* not yet initialized by DigField() */
13265 player->move_delay = player->move_delay_value;
13267 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13269 TestIfPlayerTouchesBadThing(jx, jy);
13270 TestIfPlayerTouchesCustomElement(jx, jy);
13273 if (!player->active)
13274 RemovePlayer(player);
13279 void ScrollPlayer(struct PlayerInfo *player, int mode)
13281 int jx = player->jx, jy = player->jy;
13282 int last_jx = player->last_jx, last_jy = player->last_jy;
13283 int move_stepsize = TILEX / player->move_delay_value;
13285 #if USE_NEW_PLAYER_SPEED
13286 if (!player->active)
13289 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13292 if (!player->active || player->MovPos == 0)
13296 if (mode == SCROLL_INIT)
13298 player->actual_frame_counter = FrameCounter;
13299 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13301 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13302 Feld[last_jx][last_jy] == EL_EMPTY)
13304 int last_field_block_delay = 0; /* start with no blocking at all */
13305 int block_delay_adjustment = player->block_delay_adjustment;
13307 /* if player blocks last field, add delay for exactly one move */
13308 if (player->block_last_field)
13310 last_field_block_delay += player->move_delay_value;
13312 /* when blocking enabled, prevent moving up despite gravity */
13313 #if USE_PLAYER_GRAVITY
13314 if (player->gravity && player->MovDir == MV_UP)
13315 block_delay_adjustment = -1;
13317 if (game.gravity && player->MovDir == MV_UP)
13318 block_delay_adjustment = -1;
13322 /* add block delay adjustment (also possible when not blocking) */
13323 last_field_block_delay += block_delay_adjustment;
13325 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13326 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13329 #if USE_NEW_PLAYER_SPEED
13330 if (player->MovPos != 0) /* player has not yet reached destination */
13336 else if (!FrameReached(&player->actual_frame_counter, 1))
13339 #if USE_NEW_PLAYER_SPEED
13340 if (player->MovPos != 0)
13342 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13343 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13345 /* before DrawPlayer() to draw correct player graphic for this case */
13346 if (player->MovPos == 0)
13347 CheckGravityMovement(player);
13350 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13351 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13353 /* before DrawPlayer() to draw correct player graphic for this case */
13354 if (player->MovPos == 0)
13355 CheckGravityMovement(player);
13358 if (player->MovPos == 0) /* player reached destination field */
13360 if (player->move_delay_reset_counter > 0)
13362 player->move_delay_reset_counter--;
13364 if (player->move_delay_reset_counter == 0)
13366 /* continue with normal speed after quickly moving through gate */
13367 HALVE_PLAYER_SPEED(player);
13369 /* be able to make the next move without delay */
13370 player->move_delay = 0;
13374 player->last_jx = jx;
13375 player->last_jy = jy;
13377 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13378 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13379 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13380 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13381 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13382 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13384 DrawPlayer(player); /* needed here only to cleanup last field */
13385 RemovePlayer(player);
13387 if (local_player->friends_still_needed == 0 ||
13388 IS_SP_ELEMENT(Feld[jx][jy]))
13389 PlayerWins(player);
13392 /* this breaks one level: "machine", level 000 */
13394 int move_direction = player->MovDir;
13395 int enter_side = MV_DIR_OPPOSITE(move_direction);
13396 int leave_side = move_direction;
13397 int old_jx = last_jx;
13398 int old_jy = last_jy;
13399 int old_element = Feld[old_jx][old_jy];
13400 int new_element = Feld[jx][jy];
13402 if (IS_CUSTOM_ELEMENT(old_element))
13403 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13405 player->index_bit, leave_side);
13407 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13408 CE_PLAYER_LEAVES_X,
13409 player->index_bit, leave_side);
13411 if (IS_CUSTOM_ELEMENT(new_element))
13412 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13413 player->index_bit, enter_side);
13415 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13416 CE_PLAYER_ENTERS_X,
13417 player->index_bit, enter_side);
13419 #if USE_FIX_CE_ACTION_WITH_PLAYER
13420 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13421 CE_MOVE_OF_X, move_direction);
13423 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13424 CE_MOVE_OF_X, move_direction);
13428 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13430 TestIfPlayerTouchesBadThing(jx, jy);
13431 TestIfPlayerTouchesCustomElement(jx, jy);
13433 /* needed because pushed element has not yet reached its destination,
13434 so it would trigger a change event at its previous field location */
13435 if (!player->is_pushing)
13436 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13438 if (!player->active)
13439 RemovePlayer(player);
13442 if (!local_player->LevelSolved && level.use_step_counter)
13452 if (TimeLeft <= 10 && setup.time_limit)
13453 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13456 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13458 DisplayGameControlValues();
13460 DrawGameValue_Time(TimeLeft);
13463 if (!TimeLeft && setup.time_limit)
13464 for (i = 0; i < MAX_PLAYERS; i++)
13465 KillPlayer(&stored_player[i]);
13468 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13470 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13472 DisplayGameControlValues();
13475 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13476 DrawGameValue_Time(TimePlayed);
13480 if (tape.single_step && tape.recording && !tape.pausing &&
13481 !player->programmed_action)
13482 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13486 void ScrollScreen(struct PlayerInfo *player, int mode)
13488 static unsigned long screen_frame_counter = 0;
13490 if (mode == SCROLL_INIT)
13492 /* set scrolling step size according to actual player's moving speed */
13493 ScrollStepSize = TILEX / player->move_delay_value;
13495 screen_frame_counter = FrameCounter;
13496 ScreenMovDir = player->MovDir;
13497 ScreenMovPos = player->MovPos;
13498 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13501 else if (!FrameReached(&screen_frame_counter, 1))
13506 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13507 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13508 redraw_mask |= REDRAW_FIELD;
13511 ScreenMovDir = MV_NONE;
13514 void TestIfPlayerTouchesCustomElement(int x, int y)
13516 static int xy[4][2] =
13523 static int trigger_sides[4][2] =
13525 /* center side border side */
13526 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13527 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13528 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13529 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13531 static int touch_dir[4] =
13533 MV_LEFT | MV_RIGHT,
13538 int center_element = Feld[x][y]; /* should always be non-moving! */
13541 for (i = 0; i < NUM_DIRECTIONS; i++)
13543 int xx = x + xy[i][0];
13544 int yy = y + xy[i][1];
13545 int center_side = trigger_sides[i][0];
13546 int border_side = trigger_sides[i][1];
13547 int border_element;
13549 if (!IN_LEV_FIELD(xx, yy))
13552 if (IS_PLAYER(x, y)) /* player found at center element */
13554 struct PlayerInfo *player = PLAYERINFO(x, y);
13556 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13557 border_element = Feld[xx][yy]; /* may be moving! */
13558 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13559 border_element = Feld[xx][yy];
13560 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13561 border_element = MovingOrBlocked2Element(xx, yy);
13563 continue; /* center and border element do not touch */
13565 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13566 player->index_bit, border_side);
13567 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13568 CE_PLAYER_TOUCHES_X,
13569 player->index_bit, border_side);
13571 #if USE_FIX_CE_ACTION_WITH_PLAYER
13573 /* use player element that is initially defined in the level playfield,
13574 not the player element that corresponds to the runtime player number
13575 (example: a level that contains EL_PLAYER_3 as the only player would
13576 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13577 int player_element = PLAYERINFO(x, y)->initial_element;
13579 CheckElementChangeBySide(xx, yy, border_element, player_element,
13580 CE_TOUCHING_X, border_side);
13584 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13586 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13588 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13590 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13591 continue; /* center and border element do not touch */
13594 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13595 player->index_bit, center_side);
13596 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13597 CE_PLAYER_TOUCHES_X,
13598 player->index_bit, center_side);
13600 #if USE_FIX_CE_ACTION_WITH_PLAYER
13602 /* use player element that is initially defined in the level playfield,
13603 not the player element that corresponds to the runtime player number
13604 (example: a level that contains EL_PLAYER_3 as the only player would
13605 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13606 int player_element = PLAYERINFO(xx, yy)->initial_element;
13608 CheckElementChangeBySide(x, y, center_element, player_element,
13609 CE_TOUCHING_X, center_side);
13618 #if USE_ELEMENT_TOUCHING_BUGFIX
13620 void TestIfElementTouchesCustomElement(int x, int y)
13622 static int xy[4][2] =
13629 static int trigger_sides[4][2] =
13631 /* center side border side */
13632 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13633 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13634 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13635 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13637 static int touch_dir[4] =
13639 MV_LEFT | MV_RIGHT,
13644 boolean change_center_element = FALSE;
13645 int center_element = Feld[x][y]; /* should always be non-moving! */
13646 int border_element_old[NUM_DIRECTIONS];
13649 for (i = 0; i < NUM_DIRECTIONS; i++)
13651 int xx = x + xy[i][0];
13652 int yy = y + xy[i][1];
13653 int border_element;
13655 border_element_old[i] = -1;
13657 if (!IN_LEV_FIELD(xx, yy))
13660 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13661 border_element = Feld[xx][yy]; /* may be moving! */
13662 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13663 border_element = Feld[xx][yy];
13664 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13665 border_element = MovingOrBlocked2Element(xx, yy);
13667 continue; /* center and border element do not touch */
13669 border_element_old[i] = border_element;
13672 for (i = 0; i < NUM_DIRECTIONS; i++)
13674 int xx = x + xy[i][0];
13675 int yy = y + xy[i][1];
13676 int center_side = trigger_sides[i][0];
13677 int border_element = border_element_old[i];
13679 if (border_element == -1)
13682 /* check for change of border element */
13683 CheckElementChangeBySide(xx, yy, border_element, center_element,
13684 CE_TOUCHING_X, center_side);
13686 /* (center element cannot be player, so we dont have to check this here) */
13689 for (i = 0; i < NUM_DIRECTIONS; i++)
13691 int xx = x + xy[i][0];
13692 int yy = y + xy[i][1];
13693 int border_side = trigger_sides[i][1];
13694 int border_element = border_element_old[i];
13696 if (border_element == -1)
13699 /* check for change of center element (but change it only once) */
13700 if (!change_center_element)
13701 change_center_element =
13702 CheckElementChangeBySide(x, y, center_element, border_element,
13703 CE_TOUCHING_X, border_side);
13705 #if USE_FIX_CE_ACTION_WITH_PLAYER
13706 if (IS_PLAYER(xx, yy))
13708 /* use player element that is initially defined in the level playfield,
13709 not the player element that corresponds to the runtime player number
13710 (example: a level that contains EL_PLAYER_3 as the only player would
13711 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13712 int player_element = PLAYERINFO(xx, yy)->initial_element;
13714 CheckElementChangeBySide(x, y, center_element, player_element,
13715 CE_TOUCHING_X, border_side);
13723 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13725 static int xy[4][2] =
13732 static int trigger_sides[4][2] =
13734 /* center side border side */
13735 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13736 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13737 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13738 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13740 static int touch_dir[4] =
13742 MV_LEFT | MV_RIGHT,
13747 boolean change_center_element = FALSE;
13748 int center_element = Feld[x][y]; /* should always be non-moving! */
13751 for (i = 0; i < NUM_DIRECTIONS; i++)
13753 int xx = x + xy[i][0];
13754 int yy = y + xy[i][1];
13755 int center_side = trigger_sides[i][0];
13756 int border_side = trigger_sides[i][1];
13757 int border_element;
13759 if (!IN_LEV_FIELD(xx, yy))
13762 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13763 border_element = Feld[xx][yy]; /* may be moving! */
13764 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13765 border_element = Feld[xx][yy];
13766 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13767 border_element = MovingOrBlocked2Element(xx, yy);
13769 continue; /* center and border element do not touch */
13771 /* check for change of center element (but change it only once) */
13772 if (!change_center_element)
13773 change_center_element =
13774 CheckElementChangeBySide(x, y, center_element, border_element,
13775 CE_TOUCHING_X, border_side);
13777 /* check for change of border element */
13778 CheckElementChangeBySide(xx, yy, border_element, center_element,
13779 CE_TOUCHING_X, center_side);
13785 void TestIfElementHitsCustomElement(int x, int y, int direction)
13787 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13788 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13789 int hitx = x + dx, hity = y + dy;
13790 int hitting_element = Feld[x][y];
13791 int touched_element;
13793 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13796 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13797 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13799 if (IN_LEV_FIELD(hitx, hity))
13801 int opposite_direction = MV_DIR_OPPOSITE(direction);
13802 int hitting_side = direction;
13803 int touched_side = opposite_direction;
13804 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13805 MovDir[hitx][hity] != direction ||
13806 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13812 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13813 CE_HITTING_X, touched_side);
13815 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13816 CE_HIT_BY_X, hitting_side);
13818 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13819 CE_HIT_BY_SOMETHING, opposite_direction);
13821 #if USE_FIX_CE_ACTION_WITH_PLAYER
13822 if (IS_PLAYER(hitx, hity))
13824 /* use player element that is initially defined in the level playfield,
13825 not the player element that corresponds to the runtime player number
13826 (example: a level that contains EL_PLAYER_3 as the only player would
13827 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13828 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13830 CheckElementChangeBySide(x, y, hitting_element, player_element,
13831 CE_HITTING_X, touched_side);
13837 /* "hitting something" is also true when hitting the playfield border */
13838 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13839 CE_HITTING_SOMETHING, direction);
13843 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13845 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13846 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13847 int hitx = x + dx, hity = y + dy;
13848 int hitting_element = Feld[x][y];
13849 int touched_element;
13851 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13852 !IS_FREE(hitx, hity) &&
13853 (!IS_MOVING(hitx, hity) ||
13854 MovDir[hitx][hity] != direction ||
13855 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13858 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13862 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13866 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13867 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13869 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13870 EP_CAN_SMASH_EVERYTHING, direction);
13872 if (IN_LEV_FIELD(hitx, hity))
13874 int opposite_direction = MV_DIR_OPPOSITE(direction);
13875 int hitting_side = direction;
13876 int touched_side = opposite_direction;
13878 int touched_element = MovingOrBlocked2Element(hitx, hity);
13881 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13882 MovDir[hitx][hity] != direction ||
13883 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13892 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13893 CE_SMASHED_BY_SOMETHING, opposite_direction);
13895 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13896 CE_OTHER_IS_SMASHING, touched_side);
13898 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13899 CE_OTHER_GETS_SMASHED, hitting_side);
13905 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13907 int i, kill_x = -1, kill_y = -1;
13909 int bad_element = -1;
13910 static int test_xy[4][2] =
13917 static int test_dir[4] =
13925 for (i = 0; i < NUM_DIRECTIONS; i++)
13927 int test_x, test_y, test_move_dir, test_element;
13929 test_x = good_x + test_xy[i][0];
13930 test_y = good_y + test_xy[i][1];
13932 if (!IN_LEV_FIELD(test_x, test_y))
13936 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13938 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13940 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13941 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13943 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13944 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13948 bad_element = test_element;
13954 if (kill_x != -1 || kill_y != -1)
13956 if (IS_PLAYER(good_x, good_y))
13958 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13960 if (player->shield_deadly_time_left > 0 &&
13961 !IS_INDESTRUCTIBLE(bad_element))
13962 Bang(kill_x, kill_y);
13963 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13964 KillPlayer(player);
13967 Bang(good_x, good_y);
13971 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13973 int i, kill_x = -1, kill_y = -1;
13974 int bad_element = Feld[bad_x][bad_y];
13975 static int test_xy[4][2] =
13982 static int touch_dir[4] =
13984 MV_LEFT | MV_RIGHT,
13989 static int test_dir[4] =
13997 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14000 for (i = 0; i < NUM_DIRECTIONS; i++)
14002 int test_x, test_y, test_move_dir, test_element;
14004 test_x = bad_x + test_xy[i][0];
14005 test_y = bad_y + test_xy[i][1];
14007 if (!IN_LEV_FIELD(test_x, test_y))
14011 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14013 test_element = Feld[test_x][test_y];
14015 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14016 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14018 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14019 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14021 /* good thing is player or penguin that does not move away */
14022 if (IS_PLAYER(test_x, test_y))
14024 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14026 if (bad_element == EL_ROBOT && player->is_moving)
14027 continue; /* robot does not kill player if he is moving */
14029 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14031 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14032 continue; /* center and border element do not touch */
14040 else if (test_element == EL_PENGUIN)
14050 if (kill_x != -1 || kill_y != -1)
14052 if (IS_PLAYER(kill_x, kill_y))
14054 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14056 if (player->shield_deadly_time_left > 0 &&
14057 !IS_INDESTRUCTIBLE(bad_element))
14058 Bang(bad_x, bad_y);
14059 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14060 KillPlayer(player);
14063 Bang(kill_x, kill_y);
14067 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14069 int bad_element = Feld[bad_x][bad_y];
14070 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14071 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14072 int test_x = bad_x + dx, test_y = bad_y + dy;
14073 int test_move_dir, test_element;
14074 int kill_x = -1, kill_y = -1;
14076 if (!IN_LEV_FIELD(test_x, test_y))
14080 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14082 test_element = Feld[test_x][test_y];
14084 if (test_move_dir != bad_move_dir)
14086 /* good thing can be player or penguin that does not move away */
14087 if (IS_PLAYER(test_x, test_y))
14089 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14091 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14092 player as being hit when he is moving towards the bad thing, because
14093 the "get hit by" condition would be lost after the player stops) */
14094 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14095 return; /* player moves away from bad thing */
14100 else if (test_element == EL_PENGUIN)
14107 if (kill_x != -1 || kill_y != -1)
14109 if (IS_PLAYER(kill_x, kill_y))
14111 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14113 if (player->shield_deadly_time_left > 0 &&
14114 !IS_INDESTRUCTIBLE(bad_element))
14115 Bang(bad_x, bad_y);
14116 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14117 KillPlayer(player);
14120 Bang(kill_x, kill_y);
14124 void TestIfPlayerTouchesBadThing(int x, int y)
14126 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14129 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14131 TestIfGoodThingHitsBadThing(x, y, move_dir);
14134 void TestIfBadThingTouchesPlayer(int x, int y)
14136 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14139 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14141 TestIfBadThingHitsGoodThing(x, y, move_dir);
14144 void TestIfFriendTouchesBadThing(int x, int y)
14146 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14149 void TestIfBadThingTouchesFriend(int x, int y)
14151 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14154 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14156 int i, kill_x = bad_x, kill_y = bad_y;
14157 static int xy[4][2] =
14165 for (i = 0; i < NUM_DIRECTIONS; i++)
14169 x = bad_x + xy[i][0];
14170 y = bad_y + xy[i][1];
14171 if (!IN_LEV_FIELD(x, y))
14174 element = Feld[x][y];
14175 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14176 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14184 if (kill_x != bad_x || kill_y != bad_y)
14185 Bang(bad_x, bad_y);
14188 void KillPlayer(struct PlayerInfo *player)
14190 int jx = player->jx, jy = player->jy;
14192 if (!player->active)
14195 /* the following code was introduced to prevent an infinite loop when calling
14197 -> CheckTriggeredElementChangeExt()
14198 -> ExecuteCustomElementAction()
14200 -> (infinitely repeating the above sequence of function calls)
14201 which occurs when killing the player while having a CE with the setting
14202 "kill player X when explosion of <player X>"; the solution using a new
14203 field "player->killed" was chosen for backwards compatibility, although
14204 clever use of the fields "player->active" etc. would probably also work */
14206 if (player->killed)
14210 player->killed = TRUE;
14212 /* remove accessible field at the player's position */
14213 Feld[jx][jy] = EL_EMPTY;
14215 /* deactivate shield (else Bang()/Explode() would not work right) */
14216 player->shield_normal_time_left = 0;
14217 player->shield_deadly_time_left = 0;
14221 #if USE_PLAYER_REANIMATION
14222 if (player->killed) /* player may have been reanimated */
14223 BuryPlayer(player);
14225 BuryPlayer(player);
14229 static void KillPlayerUnlessEnemyProtected(int x, int y)
14231 if (!PLAYER_ENEMY_PROTECTED(x, y))
14232 KillPlayer(PLAYERINFO(x, y));
14235 static void KillPlayerUnlessExplosionProtected(int x, int y)
14237 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14238 KillPlayer(PLAYERINFO(x, y));
14241 void BuryPlayer(struct PlayerInfo *player)
14243 int jx = player->jx, jy = player->jy;
14245 if (!player->active)
14248 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14249 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14251 player->GameOver = TRUE;
14252 RemovePlayer(player);
14255 void RemovePlayer(struct PlayerInfo *player)
14257 int jx = player->jx, jy = player->jy;
14258 int i, found = FALSE;
14260 player->present = FALSE;
14261 player->active = FALSE;
14263 if (!ExplodeField[jx][jy])
14264 StorePlayer[jx][jy] = 0;
14266 if (player->is_moving)
14267 TEST_DrawLevelField(player->last_jx, player->last_jy);
14269 for (i = 0; i < MAX_PLAYERS; i++)
14270 if (stored_player[i].active)
14274 AllPlayersGone = TRUE;
14280 #if USE_NEW_SNAP_DELAY
14281 static void setFieldForSnapping(int x, int y, int element, int direction)
14283 struct ElementInfo *ei = &element_info[element];
14284 int direction_bit = MV_DIR_TO_BIT(direction);
14285 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14286 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14287 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14289 Feld[x][y] = EL_ELEMENT_SNAPPING;
14290 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14292 ResetGfxAnimation(x, y);
14294 GfxElement[x][y] = element;
14295 GfxAction[x][y] = action;
14296 GfxDir[x][y] = direction;
14297 GfxFrame[x][y] = -1;
14302 =============================================================================
14303 checkDiagonalPushing()
14304 -----------------------------------------------------------------------------
14305 check if diagonal input device direction results in pushing of object
14306 (by checking if the alternative direction is walkable, diggable, ...)
14307 =============================================================================
14310 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14311 int x, int y, int real_dx, int real_dy)
14313 int jx, jy, dx, dy, xx, yy;
14315 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14318 /* diagonal direction: check alternative direction */
14323 xx = jx + (dx == 0 ? real_dx : 0);
14324 yy = jy + (dy == 0 ? real_dy : 0);
14326 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14330 =============================================================================
14332 -----------------------------------------------------------------------------
14333 x, y: field next to player (non-diagonal) to try to dig to
14334 real_dx, real_dy: direction as read from input device (can be diagonal)
14335 =============================================================================
14338 static int DigField(struct PlayerInfo *player,
14339 int oldx, int oldy, int x, int y,
14340 int real_dx, int real_dy, int mode)
14342 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14343 boolean player_was_pushing = player->is_pushing;
14344 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14345 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14346 int jx = oldx, jy = oldy;
14347 int dx = x - jx, dy = y - jy;
14348 int nextx = x + dx, nexty = y + dy;
14349 int move_direction = (dx == -1 ? MV_LEFT :
14350 dx == +1 ? MV_RIGHT :
14352 dy == +1 ? MV_DOWN : MV_NONE);
14353 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14354 int dig_side = MV_DIR_OPPOSITE(move_direction);
14355 int old_element = Feld[jx][jy];
14356 #if USE_FIXED_DONT_RUN_INTO
14357 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14363 if (is_player) /* function can also be called by EL_PENGUIN */
14365 if (player->MovPos == 0)
14367 player->is_digging = FALSE;
14368 player->is_collecting = FALSE;
14371 if (player->MovPos == 0) /* last pushing move finished */
14372 player->is_pushing = FALSE;
14374 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14376 player->is_switching = FALSE;
14377 player->push_delay = -1;
14379 return MP_NO_ACTION;
14383 #if !USE_FIXED_DONT_RUN_INTO
14384 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14385 return MP_NO_ACTION;
14388 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14389 old_element = Back[jx][jy];
14391 /* in case of element dropped at player position, check background */
14392 else if (Back[jx][jy] != EL_EMPTY &&
14393 game.engine_version >= VERSION_IDENT(2,2,0,0))
14394 old_element = Back[jx][jy];
14396 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14397 return MP_NO_ACTION; /* field has no opening in this direction */
14399 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14400 return MP_NO_ACTION; /* field has no opening in this direction */
14402 #if USE_FIXED_DONT_RUN_INTO
14403 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14407 Feld[jx][jy] = player->artwork_element;
14408 InitMovingField(jx, jy, MV_DOWN);
14409 Store[jx][jy] = EL_ACID;
14410 ContinueMoving(jx, jy);
14411 BuryPlayer(player);
14413 return MP_DONT_RUN_INTO;
14417 #if USE_FIXED_DONT_RUN_INTO
14418 if (player_can_move && DONT_RUN_INTO(element))
14420 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14422 return MP_DONT_RUN_INTO;
14426 #if USE_FIXED_DONT_RUN_INTO
14427 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14428 return MP_NO_ACTION;
14431 #if !USE_FIXED_DONT_RUN_INTO
14432 element = Feld[x][y];
14435 collect_count = element_info[element].collect_count_initial;
14437 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14438 return MP_NO_ACTION;
14440 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14441 player_can_move = player_can_move_or_snap;
14443 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14444 game.engine_version >= VERSION_IDENT(2,2,0,0))
14446 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14447 player->index_bit, dig_side);
14448 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14449 player->index_bit, dig_side);
14451 if (element == EL_DC_LANDMINE)
14454 if (Feld[x][y] != element) /* field changed by snapping */
14457 return MP_NO_ACTION;
14460 #if USE_PLAYER_GRAVITY
14461 if (player->gravity && is_player && !player->is_auto_moving &&
14462 canFallDown(player) && move_direction != MV_DOWN &&
14463 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14464 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14466 if (game.gravity && is_player && !player->is_auto_moving &&
14467 canFallDown(player) && move_direction != MV_DOWN &&
14468 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14469 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14472 if (player_can_move &&
14473 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14475 int sound_element = SND_ELEMENT(element);
14476 int sound_action = ACTION_WALKING;
14478 if (IS_RND_GATE(element))
14480 if (!player->key[RND_GATE_NR(element)])
14481 return MP_NO_ACTION;
14483 else if (IS_RND_GATE_GRAY(element))
14485 if (!player->key[RND_GATE_GRAY_NR(element)])
14486 return MP_NO_ACTION;
14488 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14490 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14491 return MP_NO_ACTION;
14493 else if (element == EL_EXIT_OPEN ||
14494 element == EL_EM_EXIT_OPEN ||
14495 element == EL_STEEL_EXIT_OPEN ||
14496 element == EL_EM_STEEL_EXIT_OPEN ||
14497 element == EL_SP_EXIT_OPEN ||
14498 element == EL_SP_EXIT_OPENING)
14500 sound_action = ACTION_PASSING; /* player is passing exit */
14502 else if (element == EL_EMPTY)
14504 sound_action = ACTION_MOVING; /* nothing to walk on */
14507 /* play sound from background or player, whatever is available */
14508 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14509 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14511 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14513 else if (player_can_move &&
14514 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14516 if (!ACCESS_FROM(element, opposite_direction))
14517 return MP_NO_ACTION; /* field not accessible from this direction */
14519 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14520 return MP_NO_ACTION;
14522 if (IS_EM_GATE(element))
14524 if (!player->key[EM_GATE_NR(element)])
14525 return MP_NO_ACTION;
14527 else if (IS_EM_GATE_GRAY(element))
14529 if (!player->key[EM_GATE_GRAY_NR(element)])
14530 return MP_NO_ACTION;
14532 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14534 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14535 return MP_NO_ACTION;
14537 else if (IS_EMC_GATE(element))
14539 if (!player->key[EMC_GATE_NR(element)])
14540 return MP_NO_ACTION;
14542 else if (IS_EMC_GATE_GRAY(element))
14544 if (!player->key[EMC_GATE_GRAY_NR(element)])
14545 return MP_NO_ACTION;
14547 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14549 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14550 return MP_NO_ACTION;
14552 else if (element == EL_DC_GATE_WHITE ||
14553 element == EL_DC_GATE_WHITE_GRAY ||
14554 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14556 if (player->num_white_keys == 0)
14557 return MP_NO_ACTION;
14559 player->num_white_keys--;
14561 else if (IS_SP_PORT(element))
14563 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14564 element == EL_SP_GRAVITY_PORT_RIGHT ||
14565 element == EL_SP_GRAVITY_PORT_UP ||
14566 element == EL_SP_GRAVITY_PORT_DOWN)
14567 #if USE_PLAYER_GRAVITY
14568 player->gravity = !player->gravity;
14570 game.gravity = !game.gravity;
14572 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14573 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14574 element == EL_SP_GRAVITY_ON_PORT_UP ||
14575 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14576 #if USE_PLAYER_GRAVITY
14577 player->gravity = TRUE;
14579 game.gravity = TRUE;
14581 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14582 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14583 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14584 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14585 #if USE_PLAYER_GRAVITY
14586 player->gravity = FALSE;
14588 game.gravity = FALSE;
14592 /* automatically move to the next field with double speed */
14593 player->programmed_action = move_direction;
14595 if (player->move_delay_reset_counter == 0)
14597 player->move_delay_reset_counter = 2; /* two double speed steps */
14599 DOUBLE_PLAYER_SPEED(player);
14602 PlayLevelSoundAction(x, y, ACTION_PASSING);
14604 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14608 if (mode != DF_SNAP)
14610 GfxElement[x][y] = GFX_ELEMENT(element);
14611 player->is_digging = TRUE;
14614 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14616 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14617 player->index_bit, dig_side);
14619 if (mode == DF_SNAP)
14621 #if USE_NEW_SNAP_DELAY
14622 if (level.block_snap_field)
14623 setFieldForSnapping(x, y, element, move_direction);
14625 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14627 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14630 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14631 player->index_bit, dig_side);
14634 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14638 if (is_player && mode != DF_SNAP)
14640 GfxElement[x][y] = element;
14641 player->is_collecting = TRUE;
14644 if (element == EL_SPEED_PILL)
14646 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14648 else if (element == EL_EXTRA_TIME && level.time > 0)
14650 TimeLeft += level.extra_time;
14653 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14655 DisplayGameControlValues();
14657 DrawGameValue_Time(TimeLeft);
14660 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14662 player->shield_normal_time_left += level.shield_normal_time;
14663 if (element == EL_SHIELD_DEADLY)
14664 player->shield_deadly_time_left += level.shield_deadly_time;
14666 else if (element == EL_DYNAMITE ||
14667 element == EL_EM_DYNAMITE ||
14668 element == EL_SP_DISK_RED)
14670 if (player->inventory_size < MAX_INVENTORY_SIZE)
14671 player->inventory_element[player->inventory_size++] = element;
14673 DrawGameDoorValues();
14675 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14677 player->dynabomb_count++;
14678 player->dynabombs_left++;
14680 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14682 player->dynabomb_size++;
14684 else if (element == EL_DYNABOMB_INCREASE_POWER)
14686 player->dynabomb_xl = TRUE;
14688 else if (IS_KEY(element))
14690 player->key[KEY_NR(element)] = TRUE;
14692 DrawGameDoorValues();
14694 else if (element == EL_DC_KEY_WHITE)
14696 player->num_white_keys++;
14698 /* display white keys? */
14699 /* DrawGameDoorValues(); */
14701 else if (IS_ENVELOPE(element))
14703 player->show_envelope = element;
14705 else if (element == EL_EMC_LENSES)
14707 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14709 RedrawAllInvisibleElementsForLenses();
14711 else if (element == EL_EMC_MAGNIFIER)
14713 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14715 RedrawAllInvisibleElementsForMagnifier();
14717 else if (IS_DROPPABLE(element) ||
14718 IS_THROWABLE(element)) /* can be collected and dropped */
14722 if (collect_count == 0)
14723 player->inventory_infinite_element = element;
14725 for (i = 0; i < collect_count; i++)
14726 if (player->inventory_size < MAX_INVENTORY_SIZE)
14727 player->inventory_element[player->inventory_size++] = element;
14729 DrawGameDoorValues();
14731 else if (collect_count > 0)
14733 local_player->gems_still_needed -= collect_count;
14734 if (local_player->gems_still_needed < 0)
14735 local_player->gems_still_needed = 0;
14738 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14740 DisplayGameControlValues();
14742 DrawGameValue_Emeralds(local_player->gems_still_needed);
14746 RaiseScoreElement(element);
14747 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14750 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14751 player->index_bit, dig_side);
14753 if (mode == DF_SNAP)
14755 #if USE_NEW_SNAP_DELAY
14756 if (level.block_snap_field)
14757 setFieldForSnapping(x, y, element, move_direction);
14759 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14761 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14764 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14765 player->index_bit, dig_side);
14768 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14770 if (mode == DF_SNAP && element != EL_BD_ROCK)
14771 return MP_NO_ACTION;
14773 if (CAN_FALL(element) && dy)
14774 return MP_NO_ACTION;
14776 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14777 !(element == EL_SPRING && level.use_spring_bug))
14778 return MP_NO_ACTION;
14780 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14781 ((move_direction & MV_VERTICAL &&
14782 ((element_info[element].move_pattern & MV_LEFT &&
14783 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14784 (element_info[element].move_pattern & MV_RIGHT &&
14785 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14786 (move_direction & MV_HORIZONTAL &&
14787 ((element_info[element].move_pattern & MV_UP &&
14788 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14789 (element_info[element].move_pattern & MV_DOWN &&
14790 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14791 return MP_NO_ACTION;
14793 /* do not push elements already moving away faster than player */
14794 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14795 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14796 return MP_NO_ACTION;
14798 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14800 if (player->push_delay_value == -1 || !player_was_pushing)
14801 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14803 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14805 if (player->push_delay_value == -1)
14806 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14808 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14810 if (!player->is_pushing)
14811 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14814 player->is_pushing = TRUE;
14815 player->is_active = TRUE;
14817 if (!(IN_LEV_FIELD(nextx, nexty) &&
14818 (IS_FREE(nextx, nexty) ||
14819 (IS_SB_ELEMENT(element) &&
14820 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14821 (IS_CUSTOM_ELEMENT(element) &&
14822 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14823 return MP_NO_ACTION;
14825 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14826 return MP_NO_ACTION;
14828 if (player->push_delay == -1) /* new pushing; restart delay */
14829 player->push_delay = 0;
14831 if (player->push_delay < player->push_delay_value &&
14832 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14833 element != EL_SPRING && element != EL_BALLOON)
14835 /* make sure that there is no move delay before next try to push */
14836 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14837 player->move_delay = 0;
14839 return MP_NO_ACTION;
14842 if (IS_CUSTOM_ELEMENT(element) &&
14843 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14845 if (!DigFieldByCE(nextx, nexty, element))
14846 return MP_NO_ACTION;
14849 if (IS_SB_ELEMENT(element))
14851 if (element == EL_SOKOBAN_FIELD_FULL)
14853 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14854 local_player->sokobanfields_still_needed++;
14857 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14859 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14860 local_player->sokobanfields_still_needed--;
14863 Feld[x][y] = EL_SOKOBAN_OBJECT;
14865 if (Back[x][y] == Back[nextx][nexty])
14866 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14867 else if (Back[x][y] != 0)
14868 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14871 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14874 if (local_player->sokobanfields_still_needed == 0 &&
14875 game.emulation == EMU_SOKOBAN)
14877 PlayerWins(player);
14879 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14883 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14885 InitMovingField(x, y, move_direction);
14886 GfxAction[x][y] = ACTION_PUSHING;
14888 if (mode == DF_SNAP)
14889 ContinueMoving(x, y);
14891 MovPos[x][y] = (dx != 0 ? dx : dy);
14893 Pushed[x][y] = TRUE;
14894 Pushed[nextx][nexty] = TRUE;
14896 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14897 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14899 player->push_delay_value = -1; /* get new value later */
14901 /* check for element change _after_ element has been pushed */
14902 if (game.use_change_when_pushing_bug)
14904 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14905 player->index_bit, dig_side);
14906 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14907 player->index_bit, dig_side);
14910 else if (IS_SWITCHABLE(element))
14912 if (PLAYER_SWITCHING(player, x, y))
14914 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14915 player->index_bit, dig_side);
14920 player->is_switching = TRUE;
14921 player->switch_x = x;
14922 player->switch_y = y;
14924 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14926 if (element == EL_ROBOT_WHEEL)
14928 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14932 game.robot_wheel_active = TRUE;
14934 TEST_DrawLevelField(x, y);
14936 else if (element == EL_SP_TERMINAL)
14940 SCAN_PLAYFIELD(xx, yy)
14942 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14944 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14945 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14948 else if (IS_BELT_SWITCH(element))
14950 ToggleBeltSwitch(x, y);
14952 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14953 element == EL_SWITCHGATE_SWITCH_DOWN ||
14954 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14955 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14957 ToggleSwitchgateSwitch(x, y);
14959 else if (element == EL_LIGHT_SWITCH ||
14960 element == EL_LIGHT_SWITCH_ACTIVE)
14962 ToggleLightSwitch(x, y);
14964 else if (element == EL_TIMEGATE_SWITCH ||
14965 element == EL_DC_TIMEGATE_SWITCH)
14967 ActivateTimegateSwitch(x, y);
14969 else if (element == EL_BALLOON_SWITCH_LEFT ||
14970 element == EL_BALLOON_SWITCH_RIGHT ||
14971 element == EL_BALLOON_SWITCH_UP ||
14972 element == EL_BALLOON_SWITCH_DOWN ||
14973 element == EL_BALLOON_SWITCH_NONE ||
14974 element == EL_BALLOON_SWITCH_ANY)
14976 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14977 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14978 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14979 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14980 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14983 else if (element == EL_LAMP)
14985 Feld[x][y] = EL_LAMP_ACTIVE;
14986 local_player->lights_still_needed--;
14988 ResetGfxAnimation(x, y);
14989 TEST_DrawLevelField(x, y);
14991 else if (element == EL_TIME_ORB_FULL)
14993 Feld[x][y] = EL_TIME_ORB_EMPTY;
14995 if (level.time > 0 || level.use_time_orb_bug)
14997 TimeLeft += level.time_orb_time;
15000 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15002 DisplayGameControlValues();
15004 DrawGameValue_Time(TimeLeft);
15008 ResetGfxAnimation(x, y);
15009 TEST_DrawLevelField(x, y);
15011 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15012 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15016 game.ball_state = !game.ball_state;
15018 SCAN_PLAYFIELD(xx, yy)
15020 int e = Feld[xx][yy];
15022 if (game.ball_state)
15024 if (e == EL_EMC_MAGIC_BALL)
15025 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15026 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15027 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15031 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15032 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15033 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15034 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15039 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15040 player->index_bit, dig_side);
15042 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15043 player->index_bit, dig_side);
15045 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15046 player->index_bit, dig_side);
15052 if (!PLAYER_SWITCHING(player, x, y))
15054 player->is_switching = TRUE;
15055 player->switch_x = x;
15056 player->switch_y = y;
15058 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15059 player->index_bit, dig_side);
15060 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15061 player->index_bit, dig_side);
15063 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15064 player->index_bit, dig_side);
15065 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15066 player->index_bit, dig_side);
15069 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15070 player->index_bit, dig_side);
15071 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15072 player->index_bit, dig_side);
15074 return MP_NO_ACTION;
15077 player->push_delay = -1;
15079 if (is_player) /* function can also be called by EL_PENGUIN */
15081 if (Feld[x][y] != element) /* really digged/collected something */
15083 player->is_collecting = !player->is_digging;
15084 player->is_active = TRUE;
15091 static boolean DigFieldByCE(int x, int y, int digging_element)
15093 int element = Feld[x][y];
15095 if (!IS_FREE(x, y))
15097 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15098 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15101 /* no element can dig solid indestructible elements */
15102 if (IS_INDESTRUCTIBLE(element) &&
15103 !IS_DIGGABLE(element) &&
15104 !IS_COLLECTIBLE(element))
15107 if (AmoebaNr[x][y] &&
15108 (element == EL_AMOEBA_FULL ||
15109 element == EL_BD_AMOEBA ||
15110 element == EL_AMOEBA_GROWING))
15112 AmoebaCnt[AmoebaNr[x][y]]--;
15113 AmoebaCnt2[AmoebaNr[x][y]]--;
15116 if (IS_MOVING(x, y))
15117 RemoveMovingField(x, y);
15121 TEST_DrawLevelField(x, y);
15124 /* if digged element was about to explode, prevent the explosion */
15125 ExplodeField[x][y] = EX_TYPE_NONE;
15127 PlayLevelSoundAction(x, y, action);
15130 Store[x][y] = EL_EMPTY;
15133 /* this makes it possible to leave the removed element again */
15134 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15135 Store[x][y] = element;
15137 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15139 int move_leave_element = element_info[digging_element].move_leave_element;
15141 /* this makes it possible to leave the removed element again */
15142 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15143 element : move_leave_element);
15150 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15152 int jx = player->jx, jy = player->jy;
15153 int x = jx + dx, y = jy + dy;
15154 int snap_direction = (dx == -1 ? MV_LEFT :
15155 dx == +1 ? MV_RIGHT :
15157 dy == +1 ? MV_DOWN : MV_NONE);
15158 boolean can_continue_snapping = (level.continuous_snapping &&
15159 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15161 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15164 if (!player->active || !IN_LEV_FIELD(x, y))
15172 if (player->MovPos == 0)
15173 player->is_pushing = FALSE;
15175 player->is_snapping = FALSE;
15177 if (player->MovPos == 0)
15179 player->is_moving = FALSE;
15180 player->is_digging = FALSE;
15181 player->is_collecting = FALSE;
15187 #if USE_NEW_CONTINUOUS_SNAPPING
15188 /* prevent snapping with already pressed snap key when not allowed */
15189 if (player->is_snapping && !can_continue_snapping)
15192 if (player->is_snapping)
15196 player->MovDir = snap_direction;
15198 if (player->MovPos == 0)
15200 player->is_moving = FALSE;
15201 player->is_digging = FALSE;
15202 player->is_collecting = FALSE;
15205 player->is_dropping = FALSE;
15206 player->is_dropping_pressed = FALSE;
15207 player->drop_pressed_delay = 0;
15209 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15212 player->is_snapping = TRUE;
15213 player->is_active = TRUE;
15215 if (player->MovPos == 0)
15217 player->is_moving = FALSE;
15218 player->is_digging = FALSE;
15219 player->is_collecting = FALSE;
15222 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15223 TEST_DrawLevelField(player->last_jx, player->last_jy);
15225 TEST_DrawLevelField(x, y);
15230 static boolean DropElement(struct PlayerInfo *player)
15232 int old_element, new_element;
15233 int dropx = player->jx, dropy = player->jy;
15234 int drop_direction = player->MovDir;
15235 int drop_side = drop_direction;
15237 int drop_element = get_next_dropped_element(player);
15239 int drop_element = (player->inventory_size > 0 ?
15240 player->inventory_element[player->inventory_size - 1] :
15241 player->inventory_infinite_element != EL_UNDEFINED ?
15242 player->inventory_infinite_element :
15243 player->dynabombs_left > 0 ?
15244 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15248 player->is_dropping_pressed = TRUE;
15250 /* do not drop an element on top of another element; when holding drop key
15251 pressed without moving, dropped element must move away before the next
15252 element can be dropped (this is especially important if the next element
15253 is dynamite, which can be placed on background for historical reasons) */
15254 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15257 if (IS_THROWABLE(drop_element))
15259 dropx += GET_DX_FROM_DIR(drop_direction);
15260 dropy += GET_DY_FROM_DIR(drop_direction);
15262 if (!IN_LEV_FIELD(dropx, dropy))
15266 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15267 new_element = drop_element; /* default: no change when dropping */
15269 /* check if player is active, not moving and ready to drop */
15270 if (!player->active || player->MovPos || player->drop_delay > 0)
15273 /* check if player has anything that can be dropped */
15274 if (new_element == EL_UNDEFINED)
15277 /* check if drop key was pressed long enough for EM style dynamite */
15278 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15281 /* check if anything can be dropped at the current position */
15282 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15285 /* collected custom elements can only be dropped on empty fields */
15286 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15289 if (old_element != EL_EMPTY)
15290 Back[dropx][dropy] = old_element; /* store old element on this field */
15292 ResetGfxAnimation(dropx, dropy);
15293 ResetRandomAnimationValue(dropx, dropy);
15295 if (player->inventory_size > 0 ||
15296 player->inventory_infinite_element != EL_UNDEFINED)
15298 if (player->inventory_size > 0)
15300 player->inventory_size--;
15302 DrawGameDoorValues();
15304 if (new_element == EL_DYNAMITE)
15305 new_element = EL_DYNAMITE_ACTIVE;
15306 else if (new_element == EL_EM_DYNAMITE)
15307 new_element = EL_EM_DYNAMITE_ACTIVE;
15308 else if (new_element == EL_SP_DISK_RED)
15309 new_element = EL_SP_DISK_RED_ACTIVE;
15312 Feld[dropx][dropy] = new_element;
15314 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15315 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15316 el2img(Feld[dropx][dropy]), 0);
15318 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15320 /* needed if previous element just changed to "empty" in the last frame */
15321 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15323 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15324 player->index_bit, drop_side);
15325 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15327 player->index_bit, drop_side);
15329 TestIfElementTouchesCustomElement(dropx, dropy);
15331 else /* player is dropping a dyna bomb */
15333 player->dynabombs_left--;
15335 Feld[dropx][dropy] = new_element;
15337 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15338 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15339 el2img(Feld[dropx][dropy]), 0);
15341 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15344 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15345 InitField_WithBug1(dropx, dropy, FALSE);
15347 new_element = Feld[dropx][dropy]; /* element might have changed */
15349 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15350 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15352 int move_direction, nextx, nexty;
15354 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15355 MovDir[dropx][dropy] = drop_direction;
15357 move_direction = MovDir[dropx][dropy];
15358 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15359 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15361 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15363 #if USE_FIX_IMPACT_COLLISION
15364 /* do not cause impact style collision by dropping elements that can fall */
15365 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15367 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15371 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15372 player->is_dropping = TRUE;
15374 player->drop_pressed_delay = 0;
15375 player->is_dropping_pressed = FALSE;
15377 player->drop_x = dropx;
15378 player->drop_y = dropy;
15383 /* ------------------------------------------------------------------------- */
15384 /* game sound playing functions */
15385 /* ------------------------------------------------------------------------- */
15387 static int *loop_sound_frame = NULL;
15388 static int *loop_sound_volume = NULL;
15390 void InitPlayLevelSound()
15392 int num_sounds = getSoundListSize();
15394 checked_free(loop_sound_frame);
15395 checked_free(loop_sound_volume);
15397 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15398 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15401 static void PlayLevelSound(int x, int y, int nr)
15403 int sx = SCREENX(x), sy = SCREENY(y);
15404 int volume, stereo_position;
15405 int max_distance = 8;
15406 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15408 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15409 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15412 if (!IN_LEV_FIELD(x, y) ||
15413 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15414 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15417 volume = SOUND_MAX_VOLUME;
15419 if (!IN_SCR_FIELD(sx, sy))
15421 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15422 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15424 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15427 stereo_position = (SOUND_MAX_LEFT +
15428 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15429 (SCR_FIELDX + 2 * max_distance));
15431 if (IS_LOOP_SOUND(nr))
15433 /* This assures that quieter loop sounds do not overwrite louder ones,
15434 while restarting sound volume comparison with each new game frame. */
15436 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15439 loop_sound_volume[nr] = volume;
15440 loop_sound_frame[nr] = FrameCounter;
15443 PlaySoundExt(nr, volume, stereo_position, type);
15446 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15448 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15449 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15450 y < LEVELY(BY1) ? LEVELY(BY1) :
15451 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15455 static void PlayLevelSoundAction(int x, int y, int action)
15457 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15460 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15462 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15464 if (sound_effect != SND_UNDEFINED)
15465 PlayLevelSound(x, y, sound_effect);
15468 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15471 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15473 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15474 PlayLevelSound(x, y, sound_effect);
15477 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15479 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15481 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15482 PlayLevelSound(x, y, sound_effect);
15485 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15487 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15489 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15490 StopSound(sound_effect);
15493 static void PlayLevelMusic()
15495 if (levelset.music[level_nr] != MUS_UNDEFINED)
15496 PlayMusic(levelset.music[level_nr]); /* from config file */
15498 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15501 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15503 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15504 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15505 int x = xx - 1 - offset;
15506 int y = yy - 1 - offset;
15511 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15515 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15519 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15523 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15527 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15531 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15535 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15538 case SAMPLE_android_clone:
15539 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15542 case SAMPLE_android_move:
15543 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15546 case SAMPLE_spring:
15547 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15551 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15555 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15558 case SAMPLE_eater_eat:
15559 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15563 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15566 case SAMPLE_collect:
15567 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15570 case SAMPLE_diamond:
15571 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15574 case SAMPLE_squash:
15575 /* !!! CHECK THIS !!! */
15577 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15579 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15583 case SAMPLE_wonderfall:
15584 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15588 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15592 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15596 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15600 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15604 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15608 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15611 case SAMPLE_wonder:
15612 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15616 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15619 case SAMPLE_exit_open:
15620 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15623 case SAMPLE_exit_leave:
15624 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15627 case SAMPLE_dynamite:
15628 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15632 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15636 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15640 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15644 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15648 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15652 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15656 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15662 void ChangeTime(int value)
15664 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15668 /* EMC game engine uses value from time counter of RND game engine */
15669 level.native_em_level->lev->time = *time;
15671 DrawGameValue_Time(*time);
15674 void RaiseScore(int value)
15676 /* EMC game engine and RND game engine have separate score counters */
15677 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15678 &level.native_em_level->lev->score : &local_player->score);
15682 DrawGameValue_Score(*score);
15686 void RaiseScore(int value)
15688 local_player->score += value;
15691 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15693 DisplayGameControlValues();
15695 DrawGameValue_Score(local_player->score);
15699 void RaiseScoreElement(int element)
15704 case EL_BD_DIAMOND:
15705 case EL_EMERALD_YELLOW:
15706 case EL_EMERALD_RED:
15707 case EL_EMERALD_PURPLE:
15708 case EL_SP_INFOTRON:
15709 RaiseScore(level.score[SC_EMERALD]);
15712 RaiseScore(level.score[SC_DIAMOND]);
15715 RaiseScore(level.score[SC_CRYSTAL]);
15718 RaiseScore(level.score[SC_PEARL]);
15721 case EL_BD_BUTTERFLY:
15722 case EL_SP_ELECTRON:
15723 RaiseScore(level.score[SC_BUG]);
15726 case EL_BD_FIREFLY:
15727 case EL_SP_SNIKSNAK:
15728 RaiseScore(level.score[SC_SPACESHIP]);
15731 case EL_DARK_YAMYAM:
15732 RaiseScore(level.score[SC_YAMYAM]);
15735 RaiseScore(level.score[SC_ROBOT]);
15738 RaiseScore(level.score[SC_PACMAN]);
15741 RaiseScore(level.score[SC_NUT]);
15744 case EL_EM_DYNAMITE:
15745 case EL_SP_DISK_RED:
15746 case EL_DYNABOMB_INCREASE_NUMBER:
15747 case EL_DYNABOMB_INCREASE_SIZE:
15748 case EL_DYNABOMB_INCREASE_POWER:
15749 RaiseScore(level.score[SC_DYNAMITE]);
15751 case EL_SHIELD_NORMAL:
15752 case EL_SHIELD_DEADLY:
15753 RaiseScore(level.score[SC_SHIELD]);
15755 case EL_EXTRA_TIME:
15756 RaiseScore(level.extra_time_score);
15770 case EL_DC_KEY_WHITE:
15771 RaiseScore(level.score[SC_KEY]);
15774 RaiseScore(element_info[element].collect_score);
15779 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15781 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15783 #if defined(NETWORK_AVALIABLE)
15784 if (options.network)
15785 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15794 FadeSkipNextFadeIn();
15796 fading = fading_none;
15800 OpenDoor(DOOR_CLOSE_1);
15803 game_status = GAME_MODE_MAIN;
15806 DrawAndFadeInMainMenu(REDRAW_FIELD);
15814 FadeOut(REDRAW_FIELD);
15817 game_status = GAME_MODE_MAIN;
15819 DrawAndFadeInMainMenu(REDRAW_FIELD);
15823 else /* continue playing the game */
15825 if (tape.playing && tape.deactivate_display)
15826 TapeDeactivateDisplayOff(TRUE);
15828 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15830 if (tape.playing && tape.deactivate_display)
15831 TapeDeactivateDisplayOn();
15835 void RequestQuitGame(boolean ask_if_really_quit)
15837 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15838 boolean skip_request = AllPlayersGone || quick_quit;
15840 RequestQuitGameExt(skip_request, quick_quit,
15841 "Do you really want to quit the game ?");
15845 /* ------------------------------------------------------------------------- */
15846 /* random generator functions */
15847 /* ------------------------------------------------------------------------- */
15849 unsigned int InitEngineRandom_RND(long seed)
15851 game.num_random_calls = 0;
15854 unsigned int rnd_seed = InitEngineRandom(seed);
15856 printf("::: START RND: %d\n", rnd_seed);
15861 return InitEngineRandom(seed);
15867 unsigned int RND(int max)
15871 game.num_random_calls++;
15873 return GetEngineRandom(max);
15880 /* ------------------------------------------------------------------------- */
15881 /* game engine snapshot handling functions */
15882 /* ------------------------------------------------------------------------- */
15884 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15886 struct EngineSnapshotInfo
15888 /* runtime values for custom element collect score */
15889 int collect_score[NUM_CUSTOM_ELEMENTS];
15891 /* runtime values for group element choice position */
15892 int choice_pos[NUM_GROUP_ELEMENTS];
15894 /* runtime values for belt position animations */
15895 int belt_graphic[4 * NUM_BELT_PARTS];
15896 int belt_anim_mode[4 * NUM_BELT_PARTS];
15899 struct EngineSnapshotNodeInfo
15906 static struct EngineSnapshotInfo engine_snapshot_rnd;
15907 static ListNode *engine_snapshot_list = NULL;
15908 static char *snapshot_level_identifier = NULL;
15909 static int snapshot_level_nr = -1;
15911 void FreeEngineSnapshot()
15913 while (engine_snapshot_list != NULL)
15914 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15917 setString(&snapshot_level_identifier, NULL);
15918 snapshot_level_nr = -1;
15921 static void SaveEngineSnapshotValues_RND()
15923 static int belt_base_active_element[4] =
15925 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15926 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15927 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15928 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15932 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15934 int element = EL_CUSTOM_START + i;
15936 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15939 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15941 int element = EL_GROUP_START + i;
15943 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15946 for (i = 0; i < 4; i++)
15948 for (j = 0; j < NUM_BELT_PARTS; j++)
15950 int element = belt_base_active_element[i] + j;
15951 int graphic = el2img(element);
15952 int anim_mode = graphic_info[graphic].anim_mode;
15954 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15955 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15960 static void LoadEngineSnapshotValues_RND()
15962 unsigned long num_random_calls = game.num_random_calls;
15965 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15967 int element = EL_CUSTOM_START + i;
15969 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15972 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15974 int element = EL_GROUP_START + i;
15976 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15979 for (i = 0; i < 4; i++)
15981 for (j = 0; j < NUM_BELT_PARTS; j++)
15983 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15984 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15986 graphic_info[graphic].anim_mode = anim_mode;
15990 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15992 InitRND(tape.random_seed);
15993 for (i = 0; i < num_random_calls; i++)
15997 if (game.num_random_calls != num_random_calls)
15999 Error(ERR_INFO, "number of random calls out of sync");
16000 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16001 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16002 Error(ERR_EXIT, "this should not happen -- please debug");
16006 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16008 struct EngineSnapshotNodeInfo *bi =
16009 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16011 bi->buffer_orig = buffer;
16012 bi->buffer_copy = checked_malloc(size);
16015 memcpy(bi->buffer_copy, buffer, size);
16017 addNodeToList(&engine_snapshot_list, NULL, bi);
16020 void SaveEngineSnapshot()
16022 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16024 if (level_editor_test_game) /* do not save snapshots from editor */
16027 /* copy some special values to a structure better suited for the snapshot */
16029 SaveEngineSnapshotValues_RND();
16030 SaveEngineSnapshotValues_EM();
16032 /* save values stored in special snapshot structure */
16034 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16035 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16037 /* save further RND engine values */
16039 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16040 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16041 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16043 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16044 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16045 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16046 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16048 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16049 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16050 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16051 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16052 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16054 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16055 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16056 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16058 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16060 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16062 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16063 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16065 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16066 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16067 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16068 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16069 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16070 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16071 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16072 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16073 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16074 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16075 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16076 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16077 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16078 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16079 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16080 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16081 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16082 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16084 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16085 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16087 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16088 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16089 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16091 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16092 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16094 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16095 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16096 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16097 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16098 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16100 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16101 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16103 /* save level identification information */
16105 setString(&snapshot_level_identifier, leveldir_current->identifier);
16106 snapshot_level_nr = level_nr;
16109 ListNode *node = engine_snapshot_list;
16112 while (node != NULL)
16114 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16119 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16123 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16125 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16128 void LoadEngineSnapshot()
16130 ListNode *node = engine_snapshot_list;
16132 if (engine_snapshot_list == NULL)
16135 while (node != NULL)
16137 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16142 /* restore special values from snapshot structure */
16144 LoadEngineSnapshotValues_RND();
16145 LoadEngineSnapshotValues_EM();
16148 boolean CheckEngineSnapshot()
16150 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16151 snapshot_level_nr == level_nr);
16155 /* ---------- new game button stuff ---------------------------------------- */
16157 /* graphic position values for game buttons */
16158 #define GAME_BUTTON_XSIZE 30
16159 #define GAME_BUTTON_YSIZE 30
16160 #define GAME_BUTTON_XPOS 5
16161 #define GAME_BUTTON_YPOS 215
16162 #define SOUND_BUTTON_XPOS 5
16163 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16165 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16166 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16167 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16168 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16169 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16170 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16178 } gamebutton_info[NUM_GAME_BUTTONS] =
16182 &game.button.stop.x, &game.button.stop.y,
16183 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16188 &game.button.pause.x, &game.button.pause.y,
16189 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16190 GAME_CTRL_ID_PAUSE,
16194 &game.button.play.x, &game.button.play.y,
16195 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16200 &game.button.sound_music.x, &game.button.sound_music.y,
16201 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16202 SOUND_CTRL_ID_MUSIC,
16203 "background music on/off"
16206 &game.button.sound_loops.x, &game.button.sound_loops.y,
16207 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16208 SOUND_CTRL_ID_LOOPS,
16209 "sound loops on/off"
16212 &game.button.sound_simple.x,&game.button.sound_simple.y,
16213 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16214 SOUND_CTRL_ID_SIMPLE,
16215 "normal sounds on/off"
16219 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16224 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16225 GAME_CTRL_ID_PAUSE,
16229 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16234 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16235 SOUND_CTRL_ID_MUSIC,
16236 "background music on/off"
16239 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16240 SOUND_CTRL_ID_LOOPS,
16241 "sound loops on/off"
16244 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16245 SOUND_CTRL_ID_SIMPLE,
16246 "normal sounds on/off"
16251 void CreateGameButtons()
16255 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16257 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16258 struct GadgetInfo *gi;
16261 unsigned long event_mask;
16263 int gd_xoffset, gd_yoffset;
16264 int gd_x1, gd_x2, gd_y1, gd_y2;
16267 x = DX + *gamebutton_info[i].x;
16268 y = DY + *gamebutton_info[i].y;
16269 gd_xoffset = gamebutton_info[i].gd_x;
16270 gd_yoffset = gamebutton_info[i].gd_y;
16271 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16272 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16274 if (id == GAME_CTRL_ID_STOP ||
16275 id == GAME_CTRL_ID_PAUSE ||
16276 id == GAME_CTRL_ID_PLAY)
16278 button_type = GD_TYPE_NORMAL_BUTTON;
16280 event_mask = GD_EVENT_RELEASED;
16281 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16282 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16286 button_type = GD_TYPE_CHECK_BUTTON;
16288 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16289 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16290 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16291 event_mask = GD_EVENT_PRESSED;
16292 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16293 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16296 gi = CreateGadget(GDI_CUSTOM_ID, id,
16297 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16302 GDI_X, DX + gd_xoffset,
16303 GDI_Y, DY + gd_yoffset,
16305 GDI_WIDTH, GAME_BUTTON_XSIZE,
16306 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16307 GDI_TYPE, button_type,
16308 GDI_STATE, GD_BUTTON_UNPRESSED,
16309 GDI_CHECKED, checked,
16310 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16311 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16312 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16313 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16314 GDI_DIRECT_DRAW, FALSE,
16315 GDI_EVENT_MASK, event_mask,
16316 GDI_CALLBACK_ACTION, HandleGameButtons,
16320 Error(ERR_EXIT, "cannot create gadget");
16322 game_gadget[id] = gi;
16326 void FreeGameButtons()
16330 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16331 FreeGadget(game_gadget[i]);
16334 static void MapGameButtons()
16338 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16339 MapGadget(game_gadget[i]);
16342 void UnmapGameButtons()
16346 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16347 UnmapGadget(game_gadget[i]);
16350 void RedrawGameButtons()
16354 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16355 RedrawGadget(game_gadget[i]);
16358 static void HandleGameButtons(struct GadgetInfo *gi)
16360 int id = gi->custom_id;
16362 if (game_status != GAME_MODE_PLAYING)
16367 case GAME_CTRL_ID_STOP:
16371 RequestQuitGame(TRUE);
16374 case GAME_CTRL_ID_PAUSE:
16375 if (options.network)
16377 #if defined(NETWORK_AVALIABLE)
16379 SendToServer_ContinuePlaying();
16381 SendToServer_PausePlaying();
16385 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16388 case GAME_CTRL_ID_PLAY:
16391 #if defined(NETWORK_AVALIABLE)
16392 if (options.network)
16393 SendToServer_ContinuePlaying();
16397 tape.pausing = FALSE;
16398 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16403 case SOUND_CTRL_ID_MUSIC:
16404 if (setup.sound_music)
16406 setup.sound_music = FALSE;
16409 else if (audio.music_available)
16411 setup.sound = setup.sound_music = TRUE;
16413 SetAudioMode(setup.sound);
16419 case SOUND_CTRL_ID_LOOPS:
16420 if (setup.sound_loops)
16421 setup.sound_loops = FALSE;
16422 else if (audio.loops_available)
16424 setup.sound = setup.sound_loops = TRUE;
16425 SetAudioMode(setup.sound);
16429 case SOUND_CTRL_ID_SIMPLE:
16430 if (setup.sound_simple)
16431 setup.sound_simple = FALSE;
16432 else if (audio.sound_available)
16434 setup.sound = setup.sound_simple = TRUE;
16435 SetAudioMode(setup.sound);