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_raw =
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 :
10177 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10180 if (action_arg_element_raw == EL_GROUP_START)
10181 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10184 int action_arg_direction =
10185 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10186 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10187 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10188 change->actual_trigger_side :
10189 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10190 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10193 int action_arg_number_min =
10194 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10197 int action_arg_number_max =
10198 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10199 action_type == CA_SET_LEVEL_GEMS ? 999 :
10200 action_type == CA_SET_LEVEL_TIME ? 9999 :
10201 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10202 action_type == CA_SET_CE_VALUE ? 9999 :
10203 action_type == CA_SET_CE_SCORE ? 9999 :
10206 int action_arg_number_reset =
10207 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10208 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10209 action_type == CA_SET_LEVEL_TIME ? level.time :
10210 action_type == CA_SET_LEVEL_SCORE ? 0 :
10211 #if USE_NEW_CUSTOM_VALUE
10212 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10214 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10216 action_type == CA_SET_CE_SCORE ? 0 :
10219 int action_arg_number =
10220 (action_arg <= CA_ARG_MAX ? action_arg :
10221 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10222 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10223 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10224 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10225 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10226 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10227 #if USE_NEW_CUSTOM_VALUE
10228 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10230 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10232 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10233 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10234 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10235 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10236 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10237 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10238 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10239 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10240 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10241 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10242 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10243 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10244 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10245 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10248 int action_arg_number_old =
10249 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10250 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10251 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10252 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10253 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10256 int action_arg_number_new =
10257 getModifiedActionNumber(action_arg_number_old,
10258 action_mode, action_arg_number,
10259 action_arg_number_min, action_arg_number_max);
10262 int trigger_player_bits = change->actual_trigger_player_bits;
10264 int trigger_player_bits =
10265 (change->actual_trigger_player >= EL_PLAYER_1 &&
10266 change->actual_trigger_player <= EL_PLAYER_4 ?
10267 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10271 int action_arg_player_bits =
10272 (action_arg >= CA_ARG_PLAYER_1 &&
10273 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10274 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10275 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10278 /* ---------- execute action -------------------------------------------- */
10280 switch (action_type)
10287 /* ---------- level actions ------------------------------------------- */
10289 case CA_RESTART_LEVEL:
10291 game.restart_level = TRUE;
10296 case CA_SHOW_ENVELOPE:
10298 int element = getSpecialActionElement(action_arg_element,
10299 action_arg_number, EL_ENVELOPE_1);
10301 if (IS_ENVELOPE(element))
10302 local_player->show_envelope = element;
10307 case CA_SET_LEVEL_TIME:
10309 if (level.time > 0) /* only modify limited time value */
10311 TimeLeft = action_arg_number_new;
10314 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10316 DisplayGameControlValues();
10318 DrawGameValue_Time(TimeLeft);
10321 if (!TimeLeft && setup.time_limit)
10322 for (i = 0; i < MAX_PLAYERS; i++)
10323 KillPlayer(&stored_player[i]);
10329 case CA_SET_LEVEL_SCORE:
10331 local_player->score = action_arg_number_new;
10334 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10336 DisplayGameControlValues();
10338 DrawGameValue_Score(local_player->score);
10344 case CA_SET_LEVEL_GEMS:
10346 local_player->gems_still_needed = action_arg_number_new;
10349 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10351 DisplayGameControlValues();
10353 DrawGameValue_Emeralds(local_player->gems_still_needed);
10359 #if !USE_PLAYER_GRAVITY
10360 case CA_SET_LEVEL_GRAVITY:
10362 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10363 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10364 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10370 case CA_SET_LEVEL_WIND:
10372 game.wind_direction = action_arg_direction;
10377 /* ---------- player actions ------------------------------------------ */
10379 case CA_MOVE_PLAYER:
10381 /* automatically move to the next field in specified direction */
10382 for (i = 0; i < MAX_PLAYERS; i++)
10383 if (trigger_player_bits & (1 << i))
10384 stored_player[i].programmed_action = action_arg_direction;
10389 case CA_EXIT_PLAYER:
10391 for (i = 0; i < MAX_PLAYERS; i++)
10392 if (action_arg_player_bits & (1 << i))
10393 PlayerWins(&stored_player[i]);
10398 case CA_KILL_PLAYER:
10400 for (i = 0; i < MAX_PLAYERS; i++)
10401 if (action_arg_player_bits & (1 << i))
10402 KillPlayer(&stored_player[i]);
10407 case CA_SET_PLAYER_KEYS:
10409 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10410 int element = getSpecialActionElement(action_arg_element,
10411 action_arg_number, EL_KEY_1);
10413 if (IS_KEY(element))
10415 for (i = 0; i < MAX_PLAYERS; i++)
10417 if (trigger_player_bits & (1 << i))
10419 stored_player[i].key[KEY_NR(element)] = key_state;
10421 DrawGameDoorValues();
10429 case CA_SET_PLAYER_SPEED:
10431 for (i = 0; i < MAX_PLAYERS; i++)
10433 if (trigger_player_bits & (1 << i))
10435 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10437 if (action_arg == CA_ARG_SPEED_FASTER &&
10438 stored_player[i].cannot_move)
10440 action_arg_number = STEPSIZE_VERY_SLOW;
10442 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10443 action_arg == CA_ARG_SPEED_FASTER)
10445 action_arg_number = 2;
10446 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10449 else if (action_arg == CA_ARG_NUMBER_RESET)
10451 action_arg_number = level.initial_player_stepsize[i];
10455 getModifiedActionNumber(move_stepsize,
10458 action_arg_number_min,
10459 action_arg_number_max);
10461 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10468 case CA_SET_PLAYER_SHIELD:
10470 for (i = 0; i < MAX_PLAYERS; i++)
10472 if (trigger_player_bits & (1 << i))
10474 if (action_arg == CA_ARG_SHIELD_OFF)
10476 stored_player[i].shield_normal_time_left = 0;
10477 stored_player[i].shield_deadly_time_left = 0;
10479 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10481 stored_player[i].shield_normal_time_left = 999999;
10483 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10485 stored_player[i].shield_normal_time_left = 999999;
10486 stored_player[i].shield_deadly_time_left = 999999;
10494 #if USE_PLAYER_GRAVITY
10495 case CA_SET_PLAYER_GRAVITY:
10497 for (i = 0; i < MAX_PLAYERS; i++)
10499 if (trigger_player_bits & (1 << i))
10501 stored_player[i].gravity =
10502 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10503 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10504 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10505 stored_player[i].gravity);
10513 case CA_SET_PLAYER_ARTWORK:
10515 for (i = 0; i < MAX_PLAYERS; i++)
10517 if (trigger_player_bits & (1 << i))
10519 int artwork_element = action_arg_element;
10521 if (action_arg == CA_ARG_ELEMENT_RESET)
10523 (level.use_artwork_element[i] ? level.artwork_element[i] :
10524 stored_player[i].element_nr);
10526 #if USE_GFX_RESET_PLAYER_ARTWORK
10527 if (stored_player[i].artwork_element != artwork_element)
10528 stored_player[i].Frame = 0;
10531 stored_player[i].artwork_element = artwork_element;
10533 SetPlayerWaiting(&stored_player[i], FALSE);
10535 /* set number of special actions for bored and sleeping animation */
10536 stored_player[i].num_special_action_bored =
10537 get_num_special_action(artwork_element,
10538 ACTION_BORING_1, ACTION_BORING_LAST);
10539 stored_player[i].num_special_action_sleeping =
10540 get_num_special_action(artwork_element,
10541 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10548 /* ---------- CE actions ---------------------------------------------- */
10550 case CA_SET_CE_VALUE:
10552 #if USE_NEW_CUSTOM_VALUE
10553 int last_ce_value = CustomValue[x][y];
10555 CustomValue[x][y] = action_arg_number_new;
10557 if (CustomValue[x][y] != last_ce_value)
10559 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10560 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10562 if (CustomValue[x][y] == 0)
10564 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10565 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10573 case CA_SET_CE_SCORE:
10575 #if USE_NEW_CUSTOM_VALUE
10576 int last_ce_score = ei->collect_score;
10578 ei->collect_score = action_arg_number_new;
10580 if (ei->collect_score != last_ce_score)
10582 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10583 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10585 if (ei->collect_score == 0)
10589 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10590 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10593 This is a very special case that seems to be a mixture between
10594 CheckElementChange() and CheckTriggeredElementChange(): while
10595 the first one only affects single elements that are triggered
10596 directly, the second one affects multiple elements in the playfield
10597 that are triggered indirectly by another element. This is a third
10598 case: Changing the CE score always affects multiple identical CEs,
10599 so every affected CE must be checked, not only the single CE for
10600 which the CE score was changed in the first place (as every instance
10601 of that CE shares the same CE score, and therefore also can change)!
10603 SCAN_PLAYFIELD(xx, yy)
10605 if (Feld[xx][yy] == element)
10606 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10607 CE_SCORE_GETS_ZERO);
10616 case CA_SET_CE_ARTWORK:
10618 int artwork_element = action_arg_element;
10619 boolean reset_frame = FALSE;
10622 if (action_arg == CA_ARG_ELEMENT_RESET)
10623 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10626 if (ei->gfx_element != artwork_element)
10627 reset_frame = TRUE;
10629 ei->gfx_element = artwork_element;
10631 SCAN_PLAYFIELD(xx, yy)
10633 if (Feld[xx][yy] == element)
10637 ResetGfxAnimation(xx, yy);
10638 ResetRandomAnimationValue(xx, yy);
10641 TEST_DrawLevelField(xx, yy);
10648 /* ---------- engine actions ------------------------------------------ */
10650 case CA_SET_ENGINE_SCAN_MODE:
10652 InitPlayfieldScanMode(action_arg);
10662 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10664 int old_element = Feld[x][y];
10665 int new_element = GetElementFromGroupElement(element);
10666 int previous_move_direction = MovDir[x][y];
10667 #if USE_NEW_CUSTOM_VALUE
10668 int last_ce_value = CustomValue[x][y];
10670 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10671 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10672 boolean add_player_onto_element = (new_element_is_player &&
10673 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10674 /* this breaks SnakeBite when a snake is
10675 halfway through a door that closes */
10676 /* NOW FIXED AT LEVEL INIT IN files.c */
10677 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10679 IS_WALKABLE(old_element));
10682 /* check if element under the player changes from accessible to unaccessible
10683 (needed for special case of dropping element which then changes) */
10684 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10685 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10693 if (!add_player_onto_element)
10695 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10696 RemoveMovingField(x, y);
10700 Feld[x][y] = new_element;
10702 #if !USE_GFX_RESET_GFX_ANIMATION
10703 ResetGfxAnimation(x, y);
10704 ResetRandomAnimationValue(x, y);
10707 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10708 MovDir[x][y] = previous_move_direction;
10710 #if USE_NEW_CUSTOM_VALUE
10711 if (element_info[new_element].use_last_ce_value)
10712 CustomValue[x][y] = last_ce_value;
10715 InitField_WithBug1(x, y, FALSE);
10717 new_element = Feld[x][y]; /* element may have changed */
10719 #if USE_GFX_RESET_GFX_ANIMATION
10720 ResetGfxAnimation(x, y);
10721 ResetRandomAnimationValue(x, y);
10724 TEST_DrawLevelField(x, y);
10726 if (GFX_CRUMBLED(new_element))
10727 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10731 /* check if element under the player changes from accessible to unaccessible
10732 (needed for special case of dropping element which then changes) */
10733 /* (must be checked after creating new element for walkable group elements) */
10734 #if USE_FIX_KILLED_BY_NON_WALKABLE
10735 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10736 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10743 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10744 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10753 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10754 if (new_element_is_player)
10755 RelocatePlayer(x, y, new_element);
10758 ChangeCount[x][y]++; /* count number of changes in the same frame */
10760 TestIfBadThingTouchesPlayer(x, y);
10761 TestIfPlayerTouchesCustomElement(x, y);
10762 TestIfElementTouchesCustomElement(x, y);
10765 static void CreateField(int x, int y, int element)
10767 CreateFieldExt(x, y, element, FALSE);
10770 static void CreateElementFromChange(int x, int y, int element)
10772 element = GET_VALID_RUNTIME_ELEMENT(element);
10774 #if USE_STOP_CHANGED_ELEMENTS
10775 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10777 int old_element = Feld[x][y];
10779 /* prevent changed element from moving in same engine frame
10780 unless both old and new element can either fall or move */
10781 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10782 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10787 CreateFieldExt(x, y, element, TRUE);
10790 static boolean ChangeElement(int x, int y, int element, int page)
10792 struct ElementInfo *ei = &element_info[element];
10793 struct ElementChangeInfo *change = &ei->change_page[page];
10794 int ce_value = CustomValue[x][y];
10795 int ce_score = ei->collect_score;
10796 int target_element;
10797 int old_element = Feld[x][y];
10799 /* always use default change event to prevent running into a loop */
10800 if (ChangeEvent[x][y] == -1)
10801 ChangeEvent[x][y] = CE_DELAY;
10803 if (ChangeEvent[x][y] == CE_DELAY)
10805 /* reset actual trigger element, trigger player and action element */
10806 change->actual_trigger_element = EL_EMPTY;
10807 change->actual_trigger_player = EL_PLAYER_1;
10808 change->actual_trigger_player_bits = CH_PLAYER_1;
10809 change->actual_trigger_side = CH_SIDE_NONE;
10810 change->actual_trigger_ce_value = 0;
10811 change->actual_trigger_ce_score = 0;
10814 /* do not change elements more than a specified maximum number of changes */
10815 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10818 ChangeCount[x][y]++; /* count number of changes in the same frame */
10820 if (change->explode)
10827 if (change->use_target_content)
10829 boolean complete_replace = TRUE;
10830 boolean can_replace[3][3];
10833 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10836 boolean is_walkable;
10837 boolean is_diggable;
10838 boolean is_collectible;
10839 boolean is_removable;
10840 boolean is_destructible;
10841 int ex = x + xx - 1;
10842 int ey = y + yy - 1;
10843 int content_element = change->target_content.e[xx][yy];
10846 can_replace[xx][yy] = TRUE;
10848 if (ex == x && ey == y) /* do not check changing element itself */
10851 if (content_element == EL_EMPTY_SPACE)
10853 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10858 if (!IN_LEV_FIELD(ex, ey))
10860 can_replace[xx][yy] = FALSE;
10861 complete_replace = FALSE;
10868 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10869 e = MovingOrBlocked2Element(ex, ey);
10871 is_empty = (IS_FREE(ex, ey) ||
10872 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10874 is_walkable = (is_empty || IS_WALKABLE(e));
10875 is_diggable = (is_empty || IS_DIGGABLE(e));
10876 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10877 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10878 is_removable = (is_diggable || is_collectible);
10880 can_replace[xx][yy] =
10881 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10882 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10883 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10884 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10885 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10886 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10887 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10889 if (!can_replace[xx][yy])
10890 complete_replace = FALSE;
10893 if (!change->only_if_complete || complete_replace)
10895 boolean something_has_changed = FALSE;
10897 if (change->only_if_complete && change->use_random_replace &&
10898 RND(100) < change->random_percentage)
10901 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10903 int ex = x + xx - 1;
10904 int ey = y + yy - 1;
10905 int content_element;
10907 if (can_replace[xx][yy] && (!change->use_random_replace ||
10908 RND(100) < change->random_percentage))
10910 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10911 RemoveMovingField(ex, ey);
10913 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10915 content_element = change->target_content.e[xx][yy];
10916 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10917 ce_value, ce_score);
10919 CreateElementFromChange(ex, ey, target_element);
10921 something_has_changed = TRUE;
10923 /* for symmetry reasons, freeze newly created border elements */
10924 if (ex != x || ey != y)
10925 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10929 if (something_has_changed)
10931 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10932 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10938 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10939 ce_value, ce_score);
10941 if (element == EL_DIAGONAL_GROWING ||
10942 element == EL_DIAGONAL_SHRINKING)
10944 target_element = Store[x][y];
10946 Store[x][y] = EL_EMPTY;
10949 CreateElementFromChange(x, y, target_element);
10951 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10952 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10955 /* this uses direct change before indirect change */
10956 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10961 #if USE_NEW_DELAYED_ACTION
10963 static void HandleElementChange(int x, int y, int page)
10965 int element = MovingOrBlocked2Element(x, y);
10966 struct ElementInfo *ei = &element_info[element];
10967 struct ElementChangeInfo *change = &ei->change_page[page];
10970 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10971 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10974 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10975 x, y, element, element_info[element].token_name);
10976 printf("HandleElementChange(): This should never happen!\n");
10981 /* this can happen with classic bombs on walkable, changing elements */
10982 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10985 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10986 ChangeDelay[x][y] = 0;
10992 if (ChangeDelay[x][y] == 0) /* initialize element change */
10994 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10996 if (change->can_change)
10999 /* !!! not clear why graphic animation should be reset at all here !!! */
11000 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11001 #if USE_GFX_RESET_WHEN_NOT_MOVING
11002 /* when a custom element is about to change (for example by change delay),
11003 do not reset graphic animation when the custom element is moving */
11004 if (!IS_MOVING(x, y))
11007 ResetGfxAnimation(x, y);
11008 ResetRandomAnimationValue(x, y);
11012 if (change->pre_change_function)
11013 change->pre_change_function(x, y);
11017 ChangeDelay[x][y]--;
11019 if (ChangeDelay[x][y] != 0) /* continue element change */
11021 if (change->can_change)
11023 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11025 if (IS_ANIMATED(graphic))
11026 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11028 if (change->change_function)
11029 change->change_function(x, y);
11032 else /* finish element change */
11034 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11036 page = ChangePage[x][y];
11037 ChangePage[x][y] = -1;
11039 change = &ei->change_page[page];
11042 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11044 ChangeDelay[x][y] = 1; /* try change after next move step */
11045 ChangePage[x][y] = page; /* remember page to use for change */
11050 if (change->can_change)
11052 if (ChangeElement(x, y, element, page))
11054 if (change->post_change_function)
11055 change->post_change_function(x, y);
11059 if (change->has_action)
11060 ExecuteCustomElementAction(x, y, element, page);
11066 static void HandleElementChange(int x, int y, int page)
11068 int element = MovingOrBlocked2Element(x, y);
11069 struct ElementInfo *ei = &element_info[element];
11070 struct ElementChangeInfo *change = &ei->change_page[page];
11073 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11076 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11077 x, y, element, element_info[element].token_name);
11078 printf("HandleElementChange(): This should never happen!\n");
11083 /* this can happen with classic bombs on walkable, changing elements */
11084 if (!CAN_CHANGE(element))
11087 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11088 ChangeDelay[x][y] = 0;
11094 if (ChangeDelay[x][y] == 0) /* initialize element change */
11096 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11098 ResetGfxAnimation(x, y);
11099 ResetRandomAnimationValue(x, y);
11101 if (change->pre_change_function)
11102 change->pre_change_function(x, y);
11105 ChangeDelay[x][y]--;
11107 if (ChangeDelay[x][y] != 0) /* continue element change */
11109 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11111 if (IS_ANIMATED(graphic))
11112 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11114 if (change->change_function)
11115 change->change_function(x, y);
11117 else /* finish element change */
11119 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11121 page = ChangePage[x][y];
11122 ChangePage[x][y] = -1;
11124 change = &ei->change_page[page];
11127 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11129 ChangeDelay[x][y] = 1; /* try change after next move step */
11130 ChangePage[x][y] = page; /* remember page to use for change */
11135 if (ChangeElement(x, y, element, page))
11137 if (change->post_change_function)
11138 change->post_change_function(x, y);
11145 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11146 int trigger_element,
11148 int trigger_player,
11152 boolean change_done_any = FALSE;
11153 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11156 if (!(trigger_events[trigger_element][trigger_event]))
11160 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11161 trigger_event, recursion_loop_depth, recursion_loop_detected,
11162 recursion_loop_element, EL_NAME(recursion_loop_element));
11165 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11167 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11169 int element = EL_CUSTOM_START + i;
11170 boolean change_done = FALSE;
11173 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11174 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11177 for (p = 0; p < element_info[element].num_change_pages; p++)
11179 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11181 if (change->can_change_or_has_action &&
11182 change->has_event[trigger_event] &&
11183 change->trigger_side & trigger_side &&
11184 change->trigger_player & trigger_player &&
11185 change->trigger_page & trigger_page_bits &&
11186 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11188 change->actual_trigger_element = trigger_element;
11189 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11190 change->actual_trigger_player_bits = trigger_player;
11191 change->actual_trigger_side = trigger_side;
11192 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11193 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11196 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11197 element, EL_NAME(element), p);
11200 if ((change->can_change && !change_done) || change->has_action)
11204 SCAN_PLAYFIELD(x, y)
11206 if (Feld[x][y] == element)
11208 if (change->can_change && !change_done)
11210 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11211 /* if element already changed in this frame, not only prevent
11212 another element change (checked in ChangeElement()), but
11213 also prevent additional element actions for this element */
11215 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11216 !level.use_action_after_change_bug)
11221 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11222 element, EL_NAME(element), p);
11225 ChangeDelay[x][y] = 1;
11226 ChangeEvent[x][y] = trigger_event;
11228 HandleElementChange(x, y, p);
11230 #if USE_NEW_DELAYED_ACTION
11231 else if (change->has_action)
11233 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11234 /* if element already changed in this frame, not only prevent
11235 another element change (checked in ChangeElement()), but
11236 also prevent additional element actions for this element */
11238 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11239 !level.use_action_after_change_bug)
11245 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11246 element, EL_NAME(element), p);
11249 ExecuteCustomElementAction(x, y, element, p);
11250 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11253 if (change->has_action)
11255 ExecuteCustomElementAction(x, y, element, p);
11256 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11262 if (change->can_change)
11264 change_done = TRUE;
11265 change_done_any = TRUE;
11268 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11269 element, EL_NAME(element), p);
11278 RECURSION_LOOP_DETECTION_END();
11280 return change_done_any;
11283 static boolean CheckElementChangeExt(int x, int y,
11285 int trigger_element,
11287 int trigger_player,
11290 boolean change_done = FALSE;
11293 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11294 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11297 if (Feld[x][y] == EL_BLOCKED)
11299 Blocked2Moving(x, y, &x, &y);
11300 element = Feld[x][y];
11304 /* check if element has already changed */
11305 if (Feld[x][y] != element)
11308 /* check if element has already changed or is about to change after moving */
11309 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11310 Feld[x][y] != element) ||
11312 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11313 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11314 ChangePage[x][y] != -1)))
11319 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11320 trigger_event, recursion_loop_depth, recursion_loop_detected,
11321 recursion_loop_element, EL_NAME(recursion_loop_element));
11324 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11326 for (p = 0; p < element_info[element].num_change_pages; p++)
11328 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11330 /* check trigger element for all events where the element that is checked
11331 for changing interacts with a directly adjacent element -- this is
11332 different to element changes that affect other elements to change on the
11333 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11334 boolean check_trigger_element =
11335 (trigger_event == CE_TOUCHING_X ||
11336 trigger_event == CE_HITTING_X ||
11337 trigger_event == CE_HIT_BY_X ||
11339 /* this one was forgotten until 3.2.3 */
11340 trigger_event == CE_DIGGING_X);
11343 if (change->can_change_or_has_action &&
11344 change->has_event[trigger_event] &&
11345 change->trigger_side & trigger_side &&
11346 change->trigger_player & trigger_player &&
11347 (!check_trigger_element ||
11348 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11350 change->actual_trigger_element = trigger_element;
11351 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11352 change->actual_trigger_player_bits = trigger_player;
11353 change->actual_trigger_side = trigger_side;
11354 change->actual_trigger_ce_value = CustomValue[x][y];
11355 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11357 /* special case: trigger element not at (x,y) position for some events */
11358 if (check_trigger_element)
11370 { 0, 0 }, { 0, 0 }, { 0, 0 },
11374 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11375 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11377 change->actual_trigger_ce_value = CustomValue[xx][yy];
11378 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11381 if (change->can_change && !change_done)
11383 ChangeDelay[x][y] = 1;
11384 ChangeEvent[x][y] = trigger_event;
11386 HandleElementChange(x, y, p);
11388 change_done = TRUE;
11390 #if USE_NEW_DELAYED_ACTION
11391 else if (change->has_action)
11393 ExecuteCustomElementAction(x, y, element, p);
11394 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11397 if (change->has_action)
11399 ExecuteCustomElementAction(x, y, element, p);
11400 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11406 RECURSION_LOOP_DETECTION_END();
11408 return change_done;
11411 static void PlayPlayerSound(struct PlayerInfo *player)
11413 int jx = player->jx, jy = player->jy;
11414 int sound_element = player->artwork_element;
11415 int last_action = player->last_action_waiting;
11416 int action = player->action_waiting;
11418 if (player->is_waiting)
11420 if (action != last_action)
11421 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11423 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11427 if (action != last_action)
11428 StopSound(element_info[sound_element].sound[last_action]);
11430 if (last_action == ACTION_SLEEPING)
11431 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11435 static void PlayAllPlayersSound()
11439 for (i = 0; i < MAX_PLAYERS; i++)
11440 if (stored_player[i].active)
11441 PlayPlayerSound(&stored_player[i]);
11444 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11446 boolean last_waiting = player->is_waiting;
11447 int move_dir = player->MovDir;
11449 player->dir_waiting = move_dir;
11450 player->last_action_waiting = player->action_waiting;
11454 if (!last_waiting) /* not waiting -> waiting */
11456 player->is_waiting = TRUE;
11458 player->frame_counter_bored =
11460 game.player_boring_delay_fixed +
11461 GetSimpleRandom(game.player_boring_delay_random);
11462 player->frame_counter_sleeping =
11464 game.player_sleeping_delay_fixed +
11465 GetSimpleRandom(game.player_sleeping_delay_random);
11467 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11470 if (game.player_sleeping_delay_fixed +
11471 game.player_sleeping_delay_random > 0 &&
11472 player->anim_delay_counter == 0 &&
11473 player->post_delay_counter == 0 &&
11474 FrameCounter >= player->frame_counter_sleeping)
11475 player->is_sleeping = TRUE;
11476 else if (game.player_boring_delay_fixed +
11477 game.player_boring_delay_random > 0 &&
11478 FrameCounter >= player->frame_counter_bored)
11479 player->is_bored = TRUE;
11481 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11482 player->is_bored ? ACTION_BORING :
11485 if (player->is_sleeping && player->use_murphy)
11487 /* special case for sleeping Murphy when leaning against non-free tile */
11489 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11490 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11491 !IS_MOVING(player->jx - 1, player->jy)))
11492 move_dir = MV_LEFT;
11493 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11494 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11495 !IS_MOVING(player->jx + 1, player->jy)))
11496 move_dir = MV_RIGHT;
11498 player->is_sleeping = FALSE;
11500 player->dir_waiting = move_dir;
11503 if (player->is_sleeping)
11505 if (player->num_special_action_sleeping > 0)
11507 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11509 int last_special_action = player->special_action_sleeping;
11510 int num_special_action = player->num_special_action_sleeping;
11511 int special_action =
11512 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11513 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11514 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11515 last_special_action + 1 : ACTION_SLEEPING);
11516 int special_graphic =
11517 el_act_dir2img(player->artwork_element, special_action, move_dir);
11519 player->anim_delay_counter =
11520 graphic_info[special_graphic].anim_delay_fixed +
11521 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11522 player->post_delay_counter =
11523 graphic_info[special_graphic].post_delay_fixed +
11524 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11526 player->special_action_sleeping = special_action;
11529 if (player->anim_delay_counter > 0)
11531 player->action_waiting = player->special_action_sleeping;
11532 player->anim_delay_counter--;
11534 else if (player->post_delay_counter > 0)
11536 player->post_delay_counter--;
11540 else if (player->is_bored)
11542 if (player->num_special_action_bored > 0)
11544 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11546 int special_action =
11547 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11548 int special_graphic =
11549 el_act_dir2img(player->artwork_element, special_action, move_dir);
11551 player->anim_delay_counter =
11552 graphic_info[special_graphic].anim_delay_fixed +
11553 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11554 player->post_delay_counter =
11555 graphic_info[special_graphic].post_delay_fixed +
11556 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11558 player->special_action_bored = special_action;
11561 if (player->anim_delay_counter > 0)
11563 player->action_waiting = player->special_action_bored;
11564 player->anim_delay_counter--;
11566 else if (player->post_delay_counter > 0)
11568 player->post_delay_counter--;
11573 else if (last_waiting) /* waiting -> not waiting */
11575 player->is_waiting = FALSE;
11576 player->is_bored = FALSE;
11577 player->is_sleeping = FALSE;
11579 player->frame_counter_bored = -1;
11580 player->frame_counter_sleeping = -1;
11582 player->anim_delay_counter = 0;
11583 player->post_delay_counter = 0;
11585 player->dir_waiting = player->MovDir;
11586 player->action_waiting = ACTION_DEFAULT;
11588 player->special_action_bored = ACTION_DEFAULT;
11589 player->special_action_sleeping = ACTION_DEFAULT;
11593 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11595 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11596 int left = player_action & JOY_LEFT;
11597 int right = player_action & JOY_RIGHT;
11598 int up = player_action & JOY_UP;
11599 int down = player_action & JOY_DOWN;
11600 int button1 = player_action & JOY_BUTTON_1;
11601 int button2 = player_action & JOY_BUTTON_2;
11602 int dx = (left ? -1 : right ? 1 : 0);
11603 int dy = (up ? -1 : down ? 1 : 0);
11605 if (!player->active || tape.pausing)
11611 snapped = SnapField(player, dx, dy);
11615 dropped = DropElement(player);
11617 moved = MovePlayer(player, dx, dy);
11620 if (tape.single_step && tape.recording && !tape.pausing)
11622 if (button1 || (dropped && !moved))
11624 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11625 SnapField(player, 0, 0); /* stop snapping */
11629 SetPlayerWaiting(player, FALSE);
11631 return player_action;
11635 /* no actions for this player (no input at player's configured device) */
11637 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11638 SnapField(player, 0, 0);
11639 CheckGravityMovementWhenNotMoving(player);
11641 if (player->MovPos == 0)
11642 SetPlayerWaiting(player, TRUE);
11644 if (player->MovPos == 0) /* needed for tape.playing */
11645 player->is_moving = FALSE;
11647 player->is_dropping = FALSE;
11648 player->is_dropping_pressed = FALSE;
11649 player->drop_pressed_delay = 0;
11655 static void CheckLevelTime()
11659 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11661 if (level.native_em_level->lev->home == 0) /* all players at home */
11663 PlayerWins(local_player);
11665 AllPlayersGone = TRUE;
11667 level.native_em_level->lev->home = -1;
11670 if (level.native_em_level->ply[0]->alive == 0 &&
11671 level.native_em_level->ply[1]->alive == 0 &&
11672 level.native_em_level->ply[2]->alive == 0 &&
11673 level.native_em_level->ply[3]->alive == 0) /* all dead */
11674 AllPlayersGone = TRUE;
11677 if (TimeFrames >= FRAMES_PER_SECOND)
11682 for (i = 0; i < MAX_PLAYERS; i++)
11684 struct PlayerInfo *player = &stored_player[i];
11686 if (SHIELD_ON(player))
11688 player->shield_normal_time_left--;
11690 if (player->shield_deadly_time_left > 0)
11691 player->shield_deadly_time_left--;
11695 if (!local_player->LevelSolved && !level.use_step_counter)
11703 if (TimeLeft <= 10 && setup.time_limit)
11704 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11707 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11709 DisplayGameControlValues();
11711 DrawGameValue_Time(TimeLeft);
11714 if (!TimeLeft && setup.time_limit)
11716 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11717 level.native_em_level->lev->killed_out_of_time = TRUE;
11719 for (i = 0; i < MAX_PLAYERS; i++)
11720 KillPlayer(&stored_player[i]);
11724 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11726 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11728 DisplayGameControlValues();
11731 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11732 DrawGameValue_Time(TimePlayed);
11735 level.native_em_level->lev->time =
11736 (level.time == 0 ? TimePlayed : TimeLeft);
11739 if (tape.recording || tape.playing)
11740 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11744 UpdateAndDisplayGameControlValues();
11746 UpdateGameDoorValues();
11747 DrawGameDoorValues();
11751 void AdvanceFrameAndPlayerCounters(int player_nr)
11755 /* advance frame counters (global frame counter and time frame counter) */
11759 /* advance player counters (counters for move delay, move animation etc.) */
11760 for (i = 0; i < MAX_PLAYERS; i++)
11762 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11763 int move_delay_value = stored_player[i].move_delay_value;
11764 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11766 if (!advance_player_counters) /* not all players may be affected */
11769 #if USE_NEW_PLAYER_ANIM
11770 if (move_frames == 0) /* less than one move per game frame */
11772 int stepsize = TILEX / move_delay_value;
11773 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11774 int count = (stored_player[i].is_moving ?
11775 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11777 if (count % delay == 0)
11782 stored_player[i].Frame += move_frames;
11784 if (stored_player[i].MovPos != 0)
11785 stored_player[i].StepFrame += move_frames;
11787 if (stored_player[i].move_delay > 0)
11788 stored_player[i].move_delay--;
11790 /* due to bugs in previous versions, counter must count up, not down */
11791 if (stored_player[i].push_delay != -1)
11792 stored_player[i].push_delay++;
11794 if (stored_player[i].drop_delay > 0)
11795 stored_player[i].drop_delay--;
11797 if (stored_player[i].is_dropping_pressed)
11798 stored_player[i].drop_pressed_delay++;
11802 void StartGameActions(boolean init_network_game, boolean record_tape,
11805 unsigned long new_random_seed = InitRND(random_seed);
11808 TapeStartRecording(new_random_seed);
11810 #if defined(NETWORK_AVALIABLE)
11811 if (init_network_game)
11813 SendToServer_StartPlaying();
11824 static unsigned long game_frame_delay = 0;
11825 unsigned long game_frame_delay_value;
11826 byte *recorded_player_action;
11827 byte summarized_player_action = 0;
11828 byte tape_action[MAX_PLAYERS];
11831 /* detect endless loops, caused by custom element programming */
11832 if (recursion_loop_detected && recursion_loop_depth == 0)
11834 char *message = getStringCat3("Internal Error ! Element ",
11835 EL_NAME(recursion_loop_element),
11836 " caused endless loop ! Quit the game ?");
11838 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11839 EL_NAME(recursion_loop_element));
11841 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11843 recursion_loop_detected = FALSE; /* if game should be continued */
11850 if (game.restart_level)
11851 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11853 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11855 if (level.native_em_level->lev->home == 0) /* all players at home */
11857 PlayerWins(local_player);
11859 AllPlayersGone = TRUE;
11861 level.native_em_level->lev->home = -1;
11864 if (level.native_em_level->ply[0]->alive == 0 &&
11865 level.native_em_level->ply[1]->alive == 0 &&
11866 level.native_em_level->ply[2]->alive == 0 &&
11867 level.native_em_level->ply[3]->alive == 0) /* all dead */
11868 AllPlayersGone = TRUE;
11871 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11874 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11877 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11880 game_frame_delay_value =
11881 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11883 if (tape.playing && tape.warp_forward && !tape.pausing)
11884 game_frame_delay_value = 0;
11886 /* ---------- main game synchronization point ---------- */
11888 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11890 if (network_playing && !network_player_action_received)
11892 /* try to get network player actions in time */
11894 #if defined(NETWORK_AVALIABLE)
11895 /* last chance to get network player actions without main loop delay */
11896 HandleNetworking();
11899 /* game was quit by network peer */
11900 if (game_status != GAME_MODE_PLAYING)
11903 if (!network_player_action_received)
11904 return; /* failed to get network player actions in time */
11906 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11912 /* at this point we know that we really continue executing the game */
11914 network_player_action_received = FALSE;
11916 /* when playing tape, read previously recorded player input from tape data */
11917 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11920 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11925 if (tape.set_centered_player)
11927 game.centered_player_nr_next = tape.centered_player_nr_next;
11928 game.set_centered_player = TRUE;
11931 for (i = 0; i < MAX_PLAYERS; i++)
11933 summarized_player_action |= stored_player[i].action;
11935 if (!network_playing)
11936 stored_player[i].effective_action = stored_player[i].action;
11939 #if defined(NETWORK_AVALIABLE)
11940 if (network_playing)
11941 SendToServer_MovePlayer(summarized_player_action);
11944 if (!options.network && !setup.team_mode)
11945 local_player->effective_action = summarized_player_action;
11947 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11949 for (i = 0; i < MAX_PLAYERS; i++)
11950 stored_player[i].effective_action =
11951 (i == game.centered_player_nr ? summarized_player_action : 0);
11954 if (recorded_player_action != NULL)
11955 for (i = 0; i < MAX_PLAYERS; i++)
11956 stored_player[i].effective_action = recorded_player_action[i];
11958 for (i = 0; i < MAX_PLAYERS; i++)
11960 tape_action[i] = stored_player[i].effective_action;
11962 /* (this can only happen in the R'n'D game engine) */
11963 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11964 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11967 /* only record actions from input devices, but not programmed actions */
11968 if (tape.recording)
11969 TapeRecordAction(tape_action);
11971 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11973 GameActions_EM_Main();
11981 void GameActions_EM_Main()
11983 byte effective_action[MAX_PLAYERS];
11984 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11987 for (i = 0; i < MAX_PLAYERS; i++)
11988 effective_action[i] = stored_player[i].effective_action;
11990 GameActions_EM(effective_action, warp_mode);
11994 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11997 void GameActions_RND()
11999 int magic_wall_x = 0, magic_wall_y = 0;
12000 int i, x, y, element, graphic;
12002 InitPlayfieldScanModeVars();
12004 #if USE_ONE_MORE_CHANGE_PER_FRAME
12005 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12007 SCAN_PLAYFIELD(x, y)
12009 ChangeCount[x][y] = 0;
12010 ChangeEvent[x][y] = -1;
12015 if (game.set_centered_player)
12017 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12019 /* switching to "all players" only possible if all players fit to screen */
12020 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12022 game.centered_player_nr_next = game.centered_player_nr;
12023 game.set_centered_player = FALSE;
12026 /* do not switch focus to non-existing (or non-active) player */
12027 if (game.centered_player_nr_next >= 0 &&
12028 !stored_player[game.centered_player_nr_next].active)
12030 game.centered_player_nr_next = game.centered_player_nr;
12031 game.set_centered_player = FALSE;
12035 if (game.set_centered_player &&
12036 ScreenMovPos == 0) /* screen currently aligned at tile position */
12040 if (game.centered_player_nr_next == -1)
12042 setScreenCenteredToAllPlayers(&sx, &sy);
12046 sx = stored_player[game.centered_player_nr_next].jx;
12047 sy = stored_player[game.centered_player_nr_next].jy;
12050 game.centered_player_nr = game.centered_player_nr_next;
12051 game.set_centered_player = FALSE;
12053 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12054 DrawGameDoorValues();
12057 for (i = 0; i < MAX_PLAYERS; i++)
12059 int actual_player_action = stored_player[i].effective_action;
12062 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12063 - rnd_equinox_tetrachloride 048
12064 - rnd_equinox_tetrachloride_ii 096
12065 - rnd_emanuel_schmieg 002
12066 - doctor_sloan_ww 001, 020
12068 if (stored_player[i].MovPos == 0)
12069 CheckGravityMovement(&stored_player[i]);
12072 /* overwrite programmed action with tape action */
12073 if (stored_player[i].programmed_action)
12074 actual_player_action = stored_player[i].programmed_action;
12076 PlayerActions(&stored_player[i], actual_player_action);
12078 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12081 ScrollScreen(NULL, SCROLL_GO_ON);
12083 /* for backwards compatibility, the following code emulates a fixed bug that
12084 occured when pushing elements (causing elements that just made their last
12085 pushing step to already (if possible) make their first falling step in the
12086 same game frame, which is bad); this code is also needed to use the famous
12087 "spring push bug" which is used in older levels and might be wanted to be
12088 used also in newer levels, but in this case the buggy pushing code is only
12089 affecting the "spring" element and no other elements */
12091 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12093 for (i = 0; i < MAX_PLAYERS; i++)
12095 struct PlayerInfo *player = &stored_player[i];
12096 int x = player->jx;
12097 int y = player->jy;
12099 if (player->active && player->is_pushing && player->is_moving &&
12101 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12102 Feld[x][y] == EL_SPRING))
12104 ContinueMoving(x, y);
12106 /* continue moving after pushing (this is actually a bug) */
12107 if (!IS_MOVING(x, y))
12108 Stop[x][y] = FALSE;
12114 debug_print_timestamp(0, "start main loop profiling");
12117 SCAN_PLAYFIELD(x, y)
12119 ChangeCount[x][y] = 0;
12120 ChangeEvent[x][y] = -1;
12122 /* this must be handled before main playfield loop */
12123 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12126 if (MovDelay[x][y] <= 0)
12130 #if USE_NEW_SNAP_DELAY
12131 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12134 if (MovDelay[x][y] <= 0)
12137 TEST_DrawLevelField(x, y);
12139 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12145 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12147 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12148 printf("GameActions(): This should never happen!\n");
12150 ChangePage[x][y] = -1;
12154 Stop[x][y] = FALSE;
12155 if (WasJustMoving[x][y] > 0)
12156 WasJustMoving[x][y]--;
12157 if (WasJustFalling[x][y] > 0)
12158 WasJustFalling[x][y]--;
12159 if (CheckCollision[x][y] > 0)
12160 CheckCollision[x][y]--;
12161 if (CheckImpact[x][y] > 0)
12162 CheckImpact[x][y]--;
12166 /* reset finished pushing action (not done in ContinueMoving() to allow
12167 continuous pushing animation for elements with zero push delay) */
12168 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12170 ResetGfxAnimation(x, y);
12171 TEST_DrawLevelField(x, y);
12175 if (IS_BLOCKED(x, y))
12179 Blocked2Moving(x, y, &oldx, &oldy);
12180 if (!IS_MOVING(oldx, oldy))
12182 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12183 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12184 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12185 printf("GameActions(): This should never happen!\n");
12192 debug_print_timestamp(0, "- time for pre-main loop:");
12195 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12196 SCAN_PLAYFIELD(x, y)
12198 element = Feld[x][y];
12199 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12204 int element2 = element;
12205 int graphic2 = graphic;
12207 int element2 = Feld[x][y];
12208 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12210 int last_gfx_frame = GfxFrame[x][y];
12212 if (graphic_info[graphic2].anim_global_sync)
12213 GfxFrame[x][y] = FrameCounter;
12214 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12215 GfxFrame[x][y] = CustomValue[x][y];
12216 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12217 GfxFrame[x][y] = element_info[element2].collect_score;
12218 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12219 GfxFrame[x][y] = ChangeDelay[x][y];
12221 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12222 DrawLevelGraphicAnimation(x, y, graphic2);
12225 ResetGfxFrame(x, y, TRUE);
12229 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12230 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12231 ResetRandomAnimationValue(x, y);
12235 SetRandomAnimationValue(x, y);
12239 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12242 #endif // -------------------- !!! TEST ONLY !!! --------------------
12245 debug_print_timestamp(0, "- time for TEST loop: -->");
12248 SCAN_PLAYFIELD(x, y)
12250 element = Feld[x][y];
12251 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12253 ResetGfxFrame(x, y, TRUE);
12255 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12256 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12257 ResetRandomAnimationValue(x, y);
12259 SetRandomAnimationValue(x, y);
12261 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12263 if (IS_INACTIVE(element))
12265 if (IS_ANIMATED(graphic))
12266 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12271 /* this may take place after moving, so 'element' may have changed */
12272 if (IS_CHANGING(x, y) &&
12273 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12275 int page = element_info[element].event_page_nr[CE_DELAY];
12278 HandleElementChange(x, y, page);
12280 if (CAN_CHANGE(element))
12281 HandleElementChange(x, y, page);
12283 if (HAS_ACTION(element))
12284 ExecuteCustomElementAction(x, y, element, page);
12287 element = Feld[x][y];
12288 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12291 #if 0 // ---------------------------------------------------------------------
12293 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12297 element = Feld[x][y];
12298 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12300 if (IS_ANIMATED(graphic) &&
12301 !IS_MOVING(x, y) &&
12303 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12305 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12306 TEST_DrawTwinkleOnField(x, y);
12308 else if (IS_MOVING(x, y))
12309 ContinueMoving(x, y);
12316 case EL_EM_EXIT_OPEN:
12317 case EL_SP_EXIT_OPEN:
12318 case EL_STEEL_EXIT_OPEN:
12319 case EL_EM_STEEL_EXIT_OPEN:
12320 case EL_SP_TERMINAL:
12321 case EL_SP_TERMINAL_ACTIVE:
12322 case EL_EXTRA_TIME:
12323 case EL_SHIELD_NORMAL:
12324 case EL_SHIELD_DEADLY:
12325 if (IS_ANIMATED(graphic))
12326 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12329 case EL_DYNAMITE_ACTIVE:
12330 case EL_EM_DYNAMITE_ACTIVE:
12331 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12332 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12333 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12334 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12335 case EL_SP_DISK_RED_ACTIVE:
12336 CheckDynamite(x, y);
12339 case EL_AMOEBA_GROWING:
12340 AmoebeWaechst(x, y);
12343 case EL_AMOEBA_SHRINKING:
12344 AmoebaDisappearing(x, y);
12347 #if !USE_NEW_AMOEBA_CODE
12348 case EL_AMOEBA_WET:
12349 case EL_AMOEBA_DRY:
12350 case EL_AMOEBA_FULL:
12352 case EL_EMC_DRIPPER:
12353 AmoebeAbleger(x, y);
12357 case EL_GAME_OF_LIFE:
12362 case EL_EXIT_CLOSED:
12366 case EL_EM_EXIT_CLOSED:
12370 case EL_STEEL_EXIT_CLOSED:
12371 CheckExitSteel(x, y);
12374 case EL_EM_STEEL_EXIT_CLOSED:
12375 CheckExitSteelEM(x, y);
12378 case EL_SP_EXIT_CLOSED:
12382 case EL_EXPANDABLE_WALL_GROWING:
12383 case EL_EXPANDABLE_STEELWALL_GROWING:
12384 MauerWaechst(x, y);
12387 case EL_EXPANDABLE_WALL:
12388 case EL_EXPANDABLE_WALL_HORIZONTAL:
12389 case EL_EXPANDABLE_WALL_VERTICAL:
12390 case EL_EXPANDABLE_WALL_ANY:
12391 case EL_BD_EXPANDABLE_WALL:
12392 MauerAbleger(x, y);
12395 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12396 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12397 case EL_EXPANDABLE_STEELWALL_ANY:
12398 MauerAblegerStahl(x, y);
12402 CheckForDragon(x, y);
12408 case EL_ELEMENT_SNAPPING:
12409 case EL_DIAGONAL_SHRINKING:
12410 case EL_DIAGONAL_GROWING:
12413 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12415 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12420 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12421 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12426 #else // ---------------------------------------------------------------------
12428 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12432 element = Feld[x][y];
12433 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12435 if (IS_ANIMATED(graphic) &&
12436 !IS_MOVING(x, y) &&
12438 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12440 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12441 TEST_DrawTwinkleOnField(x, y);
12443 else if ((element == EL_ACID ||
12444 element == EL_EXIT_OPEN ||
12445 element == EL_EM_EXIT_OPEN ||
12446 element == EL_SP_EXIT_OPEN ||
12447 element == EL_STEEL_EXIT_OPEN ||
12448 element == EL_EM_STEEL_EXIT_OPEN ||
12449 element == EL_SP_TERMINAL ||
12450 element == EL_SP_TERMINAL_ACTIVE ||
12451 element == EL_EXTRA_TIME ||
12452 element == EL_SHIELD_NORMAL ||
12453 element == EL_SHIELD_DEADLY) &&
12454 IS_ANIMATED(graphic))
12455 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12456 else if (IS_MOVING(x, y))
12457 ContinueMoving(x, y);
12458 else if (IS_ACTIVE_BOMB(element))
12459 CheckDynamite(x, y);
12460 else if (element == EL_AMOEBA_GROWING)
12461 AmoebeWaechst(x, y);
12462 else if (element == EL_AMOEBA_SHRINKING)
12463 AmoebaDisappearing(x, y);
12465 #if !USE_NEW_AMOEBA_CODE
12466 else if (IS_AMOEBALIVE(element))
12467 AmoebeAbleger(x, y);
12470 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12472 else if (element == EL_EXIT_CLOSED)
12474 else if (element == EL_EM_EXIT_CLOSED)
12476 else if (element == EL_STEEL_EXIT_CLOSED)
12477 CheckExitSteel(x, y);
12478 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12479 CheckExitSteelEM(x, y);
12480 else if (element == EL_SP_EXIT_CLOSED)
12482 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12483 element == EL_EXPANDABLE_STEELWALL_GROWING)
12484 MauerWaechst(x, y);
12485 else if (element == EL_EXPANDABLE_WALL ||
12486 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12487 element == EL_EXPANDABLE_WALL_VERTICAL ||
12488 element == EL_EXPANDABLE_WALL_ANY ||
12489 element == EL_BD_EXPANDABLE_WALL)
12490 MauerAbleger(x, y);
12491 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12492 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12493 element == EL_EXPANDABLE_STEELWALL_ANY)
12494 MauerAblegerStahl(x, y);
12495 else if (element == EL_FLAMES)
12496 CheckForDragon(x, y);
12497 else if (element == EL_EXPLOSION)
12498 ; /* drawing of correct explosion animation is handled separately */
12499 else if (element == EL_ELEMENT_SNAPPING ||
12500 element == EL_DIAGONAL_SHRINKING ||
12501 element == EL_DIAGONAL_GROWING)
12503 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12505 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12507 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12508 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12510 #endif // ---------------------------------------------------------------------
12512 if (IS_BELT_ACTIVE(element))
12513 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12515 if (game.magic_wall_active)
12517 int jx = local_player->jx, jy = local_player->jy;
12519 /* play the element sound at the position nearest to the player */
12520 if ((element == EL_MAGIC_WALL_FULL ||
12521 element == EL_MAGIC_WALL_ACTIVE ||
12522 element == EL_MAGIC_WALL_EMPTYING ||
12523 element == EL_BD_MAGIC_WALL_FULL ||
12524 element == EL_BD_MAGIC_WALL_ACTIVE ||
12525 element == EL_BD_MAGIC_WALL_EMPTYING ||
12526 element == EL_DC_MAGIC_WALL_FULL ||
12527 element == EL_DC_MAGIC_WALL_ACTIVE ||
12528 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12529 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12538 debug_print_timestamp(0, "- time for MAIN loop: -->");
12541 #if USE_NEW_AMOEBA_CODE
12542 /* new experimental amoeba growth stuff */
12543 if (!(FrameCounter % 8))
12545 static unsigned long random = 1684108901;
12547 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12549 x = RND(lev_fieldx);
12550 y = RND(lev_fieldy);
12551 element = Feld[x][y];
12553 if (!IS_PLAYER(x,y) &&
12554 (element == EL_EMPTY ||
12555 CAN_GROW_INTO(element) ||
12556 element == EL_QUICKSAND_EMPTY ||
12557 element == EL_QUICKSAND_FAST_EMPTY ||
12558 element == EL_ACID_SPLASH_LEFT ||
12559 element == EL_ACID_SPLASH_RIGHT))
12561 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12562 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12563 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12564 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12565 Feld[x][y] = EL_AMOEBA_DROP;
12568 random = random * 129 + 1;
12574 if (game.explosions_delayed)
12577 game.explosions_delayed = FALSE;
12579 SCAN_PLAYFIELD(x, y)
12581 element = Feld[x][y];
12583 if (ExplodeField[x][y])
12584 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12585 else if (element == EL_EXPLOSION)
12586 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12588 ExplodeField[x][y] = EX_TYPE_NONE;
12591 game.explosions_delayed = TRUE;
12594 if (game.magic_wall_active)
12596 if (!(game.magic_wall_time_left % 4))
12598 int element = Feld[magic_wall_x][magic_wall_y];
12600 if (element == EL_BD_MAGIC_WALL_FULL ||
12601 element == EL_BD_MAGIC_WALL_ACTIVE ||
12602 element == EL_BD_MAGIC_WALL_EMPTYING)
12603 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12604 else if (element == EL_DC_MAGIC_WALL_FULL ||
12605 element == EL_DC_MAGIC_WALL_ACTIVE ||
12606 element == EL_DC_MAGIC_WALL_EMPTYING)
12607 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12609 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12612 if (game.magic_wall_time_left > 0)
12614 game.magic_wall_time_left--;
12616 if (!game.magic_wall_time_left)
12618 SCAN_PLAYFIELD(x, y)
12620 element = Feld[x][y];
12622 if (element == EL_MAGIC_WALL_ACTIVE ||
12623 element == EL_MAGIC_WALL_FULL)
12625 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12626 TEST_DrawLevelField(x, y);
12628 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12629 element == EL_BD_MAGIC_WALL_FULL)
12631 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12632 TEST_DrawLevelField(x, y);
12634 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12635 element == EL_DC_MAGIC_WALL_FULL)
12637 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12638 TEST_DrawLevelField(x, y);
12642 game.magic_wall_active = FALSE;
12647 if (game.light_time_left > 0)
12649 game.light_time_left--;
12651 if (game.light_time_left == 0)
12652 RedrawAllLightSwitchesAndInvisibleElements();
12655 if (game.timegate_time_left > 0)
12657 game.timegate_time_left--;
12659 if (game.timegate_time_left == 0)
12660 CloseAllOpenTimegates();
12663 if (game.lenses_time_left > 0)
12665 game.lenses_time_left--;
12667 if (game.lenses_time_left == 0)
12668 RedrawAllInvisibleElementsForLenses();
12671 if (game.magnify_time_left > 0)
12673 game.magnify_time_left--;
12675 if (game.magnify_time_left == 0)
12676 RedrawAllInvisibleElementsForMagnifier();
12679 for (i = 0; i < MAX_PLAYERS; i++)
12681 struct PlayerInfo *player = &stored_player[i];
12683 if (SHIELD_ON(player))
12685 if (player->shield_deadly_time_left)
12686 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12687 else if (player->shield_normal_time_left)
12688 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12692 #if USE_DELAYED_GFX_REDRAW
12693 SCAN_PLAYFIELD(x, y)
12696 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12698 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12699 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12702 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12703 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12705 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12706 DrawLevelField(x, y);
12708 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12709 DrawLevelFieldCrumbledSand(x, y);
12711 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12712 DrawLevelFieldCrumbledSandNeighbours(x, y);
12714 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12715 DrawTwinkleOnField(x, y);
12718 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12725 PlayAllPlayersSound();
12727 if (options.debug) /* calculate frames per second */
12729 static unsigned long fps_counter = 0;
12730 static int fps_frames = 0;
12731 unsigned long fps_delay_ms = Counter() - fps_counter;
12735 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12737 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12740 fps_counter = Counter();
12743 redraw_mask |= REDRAW_FPS;
12746 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12748 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12750 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12752 local_player->show_envelope = 0;
12756 debug_print_timestamp(0, "stop main loop profiling ");
12757 printf("----------------------------------------------------------\n");
12760 /* use random number generator in every frame to make it less predictable */
12761 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12765 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12767 int min_x = x, min_y = y, max_x = x, max_y = y;
12770 for (i = 0; i < MAX_PLAYERS; i++)
12772 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12774 if (!stored_player[i].active || &stored_player[i] == player)
12777 min_x = MIN(min_x, jx);
12778 min_y = MIN(min_y, jy);
12779 max_x = MAX(max_x, jx);
12780 max_y = MAX(max_y, jy);
12783 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12786 static boolean AllPlayersInVisibleScreen()
12790 for (i = 0; i < MAX_PLAYERS; i++)
12792 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12794 if (!stored_player[i].active)
12797 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12804 void ScrollLevel(int dx, int dy)
12807 /* (directly solved in BlitBitmap() now) */
12808 static Bitmap *bitmap_db_field2 = NULL;
12809 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12816 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12817 /* only horizontal XOR vertical scroll direction allowed */
12818 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12823 /* (directly solved in BlitBitmap() now) */
12824 if (bitmap_db_field2 == NULL)
12825 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12827 /* needed when blitting directly to same bitmap -- should not be needed with
12828 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12829 BlitBitmap(drawto_field, bitmap_db_field2,
12830 FX + TILEX * (dx == -1) - softscroll_offset,
12831 FY + TILEY * (dy == -1) - softscroll_offset,
12832 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12833 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12834 FX + TILEX * (dx == 1) - softscroll_offset,
12835 FY + TILEY * (dy == 1) - softscroll_offset);
12836 BlitBitmap(bitmap_db_field2, drawto_field,
12837 FX + TILEX * (dx == 1) - softscroll_offset,
12838 FY + TILEY * (dy == 1) - softscroll_offset,
12839 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12840 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12841 FX + TILEX * (dx == 1) - softscroll_offset,
12842 FY + TILEY * (dy == 1) - softscroll_offset);
12847 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12848 int xsize = (BX2 - BX1 + 1);
12849 int ysize = (BY2 - BY1 + 1);
12850 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12851 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12852 int step = (start < end ? +1 : -1);
12854 for (i = start; i != end; i += step)
12856 BlitBitmap(drawto_field, drawto_field,
12857 FX + TILEX * (dx != 0 ? i + step : 0),
12858 FY + TILEY * (dy != 0 ? i + step : 0),
12859 TILEX * (dx != 0 ? 1 : xsize),
12860 TILEY * (dy != 0 ? 1 : ysize),
12861 FX + TILEX * (dx != 0 ? i : 0),
12862 FY + TILEY * (dy != 0 ? i : 0));
12867 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12869 BlitBitmap(drawto_field, drawto_field,
12870 FX + TILEX * (dx == -1) - softscroll_offset,
12871 FY + TILEY * (dy == -1) - softscroll_offset,
12872 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12873 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12874 FX + TILEX * (dx == 1) - softscroll_offset,
12875 FY + TILEY * (dy == 1) - softscroll_offset);
12881 x = (dx == 1 ? BX1 : BX2);
12882 for (y = BY1; y <= BY2; y++)
12883 DrawScreenField(x, y);
12888 y = (dy == 1 ? BY1 : BY2);
12889 for (x = BX1; x <= BX2; x++)
12890 DrawScreenField(x, y);
12893 redraw_mask |= REDRAW_FIELD;
12896 static boolean canFallDown(struct PlayerInfo *player)
12898 int jx = player->jx, jy = player->jy;
12900 return (IN_LEV_FIELD(jx, jy + 1) &&
12901 (IS_FREE(jx, jy + 1) ||
12902 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12903 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12904 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12907 static boolean canPassField(int x, int y, int move_dir)
12909 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12910 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12911 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12912 int nextx = x + dx;
12913 int nexty = y + dy;
12914 int element = Feld[x][y];
12916 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12917 !CAN_MOVE(element) &&
12918 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12919 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12920 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12923 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12925 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12926 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12927 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12931 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12932 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12933 (IS_DIGGABLE(Feld[newx][newy]) ||
12934 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12935 canPassField(newx, newy, move_dir)));
12938 static void CheckGravityMovement(struct PlayerInfo *player)
12940 #if USE_PLAYER_GRAVITY
12941 if (player->gravity && !player->programmed_action)
12943 if (game.gravity && !player->programmed_action)
12946 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12947 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12948 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12949 int jx = player->jx, jy = player->jy;
12950 boolean player_is_moving_to_valid_field =
12951 (!player_is_snapping &&
12952 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12953 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12954 boolean player_can_fall_down = canFallDown(player);
12956 if (player_can_fall_down &&
12957 !player_is_moving_to_valid_field)
12958 player->programmed_action = MV_DOWN;
12962 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12964 return CheckGravityMovement(player);
12966 #if USE_PLAYER_GRAVITY
12967 if (player->gravity && !player->programmed_action)
12969 if (game.gravity && !player->programmed_action)
12972 int jx = player->jx, jy = player->jy;
12973 boolean field_under_player_is_free =
12974 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12975 boolean player_is_standing_on_valid_field =
12976 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12977 (IS_WALKABLE(Feld[jx][jy]) &&
12978 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12980 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12981 player->programmed_action = MV_DOWN;
12986 MovePlayerOneStep()
12987 -----------------------------------------------------------------------------
12988 dx, dy: direction (non-diagonal) to try to move the player to
12989 real_dx, real_dy: direction as read from input device (can be diagonal)
12992 boolean MovePlayerOneStep(struct PlayerInfo *player,
12993 int dx, int dy, int real_dx, int real_dy)
12995 int jx = player->jx, jy = player->jy;
12996 int new_jx = jx + dx, new_jy = jy + dy;
12997 #if !USE_FIXED_DONT_RUN_INTO
13001 boolean player_can_move = !player->cannot_move;
13003 if (!player->active || (!dx && !dy))
13004 return MP_NO_ACTION;
13006 player->MovDir = (dx < 0 ? MV_LEFT :
13007 dx > 0 ? MV_RIGHT :
13009 dy > 0 ? MV_DOWN : MV_NONE);
13011 if (!IN_LEV_FIELD(new_jx, new_jy))
13012 return MP_NO_ACTION;
13014 if (!player_can_move)
13016 if (player->MovPos == 0)
13018 player->is_moving = FALSE;
13019 player->is_digging = FALSE;
13020 player->is_collecting = FALSE;
13021 player->is_snapping = FALSE;
13022 player->is_pushing = FALSE;
13027 if (!options.network && game.centered_player_nr == -1 &&
13028 !AllPlayersInSight(player, new_jx, new_jy))
13029 return MP_NO_ACTION;
13031 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13032 return MP_NO_ACTION;
13035 #if !USE_FIXED_DONT_RUN_INTO
13036 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13038 /* (moved to DigField()) */
13039 if (player_can_move && DONT_RUN_INTO(element))
13041 if (element == EL_ACID && dx == 0 && dy == 1)
13043 SplashAcid(new_jx, new_jy);
13044 Feld[jx][jy] = EL_PLAYER_1;
13045 InitMovingField(jx, jy, MV_DOWN);
13046 Store[jx][jy] = EL_ACID;
13047 ContinueMoving(jx, jy);
13048 BuryPlayer(player);
13051 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13057 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13058 if (can_move != MP_MOVING)
13061 /* check if DigField() has caused relocation of the player */
13062 if (player->jx != jx || player->jy != jy)
13063 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13065 StorePlayer[jx][jy] = 0;
13066 player->last_jx = jx;
13067 player->last_jy = jy;
13068 player->jx = new_jx;
13069 player->jy = new_jy;
13070 StorePlayer[new_jx][new_jy] = player->element_nr;
13072 if (player->move_delay_value_next != -1)
13074 player->move_delay_value = player->move_delay_value_next;
13075 player->move_delay_value_next = -1;
13079 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13081 player->step_counter++;
13083 PlayerVisit[jx][jy] = FrameCounter;
13085 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13086 player->is_moving = TRUE;
13090 /* should better be called in MovePlayer(), but this breaks some tapes */
13091 ScrollPlayer(player, SCROLL_INIT);
13097 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13099 int jx = player->jx, jy = player->jy;
13100 int old_jx = jx, old_jy = jy;
13101 int moved = MP_NO_ACTION;
13103 if (!player->active)
13108 if (player->MovPos == 0)
13110 player->is_moving = FALSE;
13111 player->is_digging = FALSE;
13112 player->is_collecting = FALSE;
13113 player->is_snapping = FALSE;
13114 player->is_pushing = FALSE;
13120 if (player->move_delay > 0)
13123 player->move_delay = -1; /* set to "uninitialized" value */
13125 /* store if player is automatically moved to next field */
13126 player->is_auto_moving = (player->programmed_action != MV_NONE);
13128 /* remove the last programmed player action */
13129 player->programmed_action = 0;
13131 if (player->MovPos)
13133 /* should only happen if pre-1.2 tape recordings are played */
13134 /* this is only for backward compatibility */
13136 int original_move_delay_value = player->move_delay_value;
13139 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13143 /* scroll remaining steps with finest movement resolution */
13144 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13146 while (player->MovPos)
13148 ScrollPlayer(player, SCROLL_GO_ON);
13149 ScrollScreen(NULL, SCROLL_GO_ON);
13151 AdvanceFrameAndPlayerCounters(player->index_nr);
13157 player->move_delay_value = original_move_delay_value;
13160 player->is_active = FALSE;
13162 if (player->last_move_dir & MV_HORIZONTAL)
13164 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13165 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13169 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13170 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13173 #if USE_FIXED_BORDER_RUNNING_GFX
13174 if (!moved && !player->is_active)
13176 player->is_moving = FALSE;
13177 player->is_digging = FALSE;
13178 player->is_collecting = FALSE;
13179 player->is_snapping = FALSE;
13180 player->is_pushing = FALSE;
13188 if (moved & MP_MOVING && !ScreenMovPos &&
13189 (player->index_nr == game.centered_player_nr ||
13190 game.centered_player_nr == -1))
13192 if (moved & MP_MOVING && !ScreenMovPos &&
13193 (player == local_player || !options.network))
13196 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13197 int offset = game.scroll_delay_value;
13199 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13201 /* actual player has left the screen -- scroll in that direction */
13202 if (jx != old_jx) /* player has moved horizontally */
13203 scroll_x += (jx - old_jx);
13204 else /* player has moved vertically */
13205 scroll_y += (jy - old_jy);
13209 if (jx != old_jx) /* player has moved horizontally */
13211 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13212 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13213 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13215 /* don't scroll over playfield boundaries */
13216 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13217 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13219 /* don't scroll more than one field at a time */
13220 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13222 /* don't scroll against the player's moving direction */
13223 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13224 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13225 scroll_x = old_scroll_x;
13227 else /* player has moved vertically */
13229 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13230 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13231 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13233 /* don't scroll over playfield boundaries */
13234 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13235 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13237 /* don't scroll more than one field at a time */
13238 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13240 /* don't scroll against the player's moving direction */
13241 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13242 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13243 scroll_y = old_scroll_y;
13247 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13250 if (!options.network && game.centered_player_nr == -1 &&
13251 !AllPlayersInVisibleScreen())
13253 scroll_x = old_scroll_x;
13254 scroll_y = old_scroll_y;
13258 if (!options.network && !AllPlayersInVisibleScreen())
13260 scroll_x = old_scroll_x;
13261 scroll_y = old_scroll_y;
13266 ScrollScreen(player, SCROLL_INIT);
13267 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13272 player->StepFrame = 0;
13274 if (moved & MP_MOVING)
13276 if (old_jx != jx && old_jy == jy)
13277 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13278 else if (old_jx == jx && old_jy != jy)
13279 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13281 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13283 player->last_move_dir = player->MovDir;
13284 player->is_moving = TRUE;
13285 player->is_snapping = FALSE;
13286 player->is_switching = FALSE;
13287 player->is_dropping = FALSE;
13288 player->is_dropping_pressed = FALSE;
13289 player->drop_pressed_delay = 0;
13292 /* should better be called here than above, but this breaks some tapes */
13293 ScrollPlayer(player, SCROLL_INIT);
13298 CheckGravityMovementWhenNotMoving(player);
13300 player->is_moving = FALSE;
13302 /* at this point, the player is allowed to move, but cannot move right now
13303 (e.g. because of something blocking the way) -- ensure that the player
13304 is also allowed to move in the next frame (in old versions before 3.1.1,
13305 the player was forced to wait again for eight frames before next try) */
13307 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13308 player->move_delay = 0; /* allow direct movement in the next frame */
13311 if (player->move_delay == -1) /* not yet initialized by DigField() */
13312 player->move_delay = player->move_delay_value;
13314 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13316 TestIfPlayerTouchesBadThing(jx, jy);
13317 TestIfPlayerTouchesCustomElement(jx, jy);
13320 if (!player->active)
13321 RemovePlayer(player);
13326 void ScrollPlayer(struct PlayerInfo *player, int mode)
13328 int jx = player->jx, jy = player->jy;
13329 int last_jx = player->last_jx, last_jy = player->last_jy;
13330 int move_stepsize = TILEX / player->move_delay_value;
13332 #if USE_NEW_PLAYER_SPEED
13333 if (!player->active)
13336 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13339 if (!player->active || player->MovPos == 0)
13343 if (mode == SCROLL_INIT)
13345 player->actual_frame_counter = FrameCounter;
13346 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13348 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13349 Feld[last_jx][last_jy] == EL_EMPTY)
13351 int last_field_block_delay = 0; /* start with no blocking at all */
13352 int block_delay_adjustment = player->block_delay_adjustment;
13354 /* if player blocks last field, add delay for exactly one move */
13355 if (player->block_last_field)
13357 last_field_block_delay += player->move_delay_value;
13359 /* when blocking enabled, prevent moving up despite gravity */
13360 #if USE_PLAYER_GRAVITY
13361 if (player->gravity && player->MovDir == MV_UP)
13362 block_delay_adjustment = -1;
13364 if (game.gravity && player->MovDir == MV_UP)
13365 block_delay_adjustment = -1;
13369 /* add block delay adjustment (also possible when not blocking) */
13370 last_field_block_delay += block_delay_adjustment;
13372 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13373 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13376 #if USE_NEW_PLAYER_SPEED
13377 if (player->MovPos != 0) /* player has not yet reached destination */
13383 else if (!FrameReached(&player->actual_frame_counter, 1))
13386 #if USE_NEW_PLAYER_SPEED
13387 if (player->MovPos != 0)
13389 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13390 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13392 /* before DrawPlayer() to draw correct player graphic for this case */
13393 if (player->MovPos == 0)
13394 CheckGravityMovement(player);
13397 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13398 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13400 /* before DrawPlayer() to draw correct player graphic for this case */
13401 if (player->MovPos == 0)
13402 CheckGravityMovement(player);
13405 if (player->MovPos == 0) /* player reached destination field */
13407 if (player->move_delay_reset_counter > 0)
13409 player->move_delay_reset_counter--;
13411 if (player->move_delay_reset_counter == 0)
13413 /* continue with normal speed after quickly moving through gate */
13414 HALVE_PLAYER_SPEED(player);
13416 /* be able to make the next move without delay */
13417 player->move_delay = 0;
13421 player->last_jx = jx;
13422 player->last_jy = jy;
13424 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13425 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13426 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13427 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13428 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13429 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13431 DrawPlayer(player); /* needed here only to cleanup last field */
13432 RemovePlayer(player);
13434 if (local_player->friends_still_needed == 0 ||
13435 IS_SP_ELEMENT(Feld[jx][jy]))
13436 PlayerWins(player);
13439 /* this breaks one level: "machine", level 000 */
13441 int move_direction = player->MovDir;
13442 int enter_side = MV_DIR_OPPOSITE(move_direction);
13443 int leave_side = move_direction;
13444 int old_jx = last_jx;
13445 int old_jy = last_jy;
13446 int old_element = Feld[old_jx][old_jy];
13447 int new_element = Feld[jx][jy];
13449 if (IS_CUSTOM_ELEMENT(old_element))
13450 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13452 player->index_bit, leave_side);
13454 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13455 CE_PLAYER_LEAVES_X,
13456 player->index_bit, leave_side);
13458 if (IS_CUSTOM_ELEMENT(new_element))
13459 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13460 player->index_bit, enter_side);
13462 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13463 CE_PLAYER_ENTERS_X,
13464 player->index_bit, enter_side);
13466 #if USE_FIX_CE_ACTION_WITH_PLAYER
13467 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13468 CE_MOVE_OF_X, move_direction);
13470 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13471 CE_MOVE_OF_X, move_direction);
13475 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13477 TestIfPlayerTouchesBadThing(jx, jy);
13478 TestIfPlayerTouchesCustomElement(jx, jy);
13480 /* needed because pushed element has not yet reached its destination,
13481 so it would trigger a change event at its previous field location */
13482 if (!player->is_pushing)
13483 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13485 if (!player->active)
13486 RemovePlayer(player);
13489 if (!local_player->LevelSolved && level.use_step_counter)
13499 if (TimeLeft <= 10 && setup.time_limit)
13500 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13503 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13505 DisplayGameControlValues();
13507 DrawGameValue_Time(TimeLeft);
13510 if (!TimeLeft && setup.time_limit)
13511 for (i = 0; i < MAX_PLAYERS; i++)
13512 KillPlayer(&stored_player[i]);
13515 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13517 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13519 DisplayGameControlValues();
13522 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13523 DrawGameValue_Time(TimePlayed);
13527 if (tape.single_step && tape.recording && !tape.pausing &&
13528 !player->programmed_action)
13529 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13533 void ScrollScreen(struct PlayerInfo *player, int mode)
13535 static unsigned long screen_frame_counter = 0;
13537 if (mode == SCROLL_INIT)
13539 /* set scrolling step size according to actual player's moving speed */
13540 ScrollStepSize = TILEX / player->move_delay_value;
13542 screen_frame_counter = FrameCounter;
13543 ScreenMovDir = player->MovDir;
13544 ScreenMovPos = player->MovPos;
13545 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13548 else if (!FrameReached(&screen_frame_counter, 1))
13553 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13554 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13555 redraw_mask |= REDRAW_FIELD;
13558 ScreenMovDir = MV_NONE;
13561 void TestIfPlayerTouchesCustomElement(int x, int y)
13563 static int xy[4][2] =
13570 static int trigger_sides[4][2] =
13572 /* center side border side */
13573 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13574 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13575 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13576 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13578 static int touch_dir[4] =
13580 MV_LEFT | MV_RIGHT,
13585 int center_element = Feld[x][y]; /* should always be non-moving! */
13588 for (i = 0; i < NUM_DIRECTIONS; i++)
13590 int xx = x + xy[i][0];
13591 int yy = y + xy[i][1];
13592 int center_side = trigger_sides[i][0];
13593 int border_side = trigger_sides[i][1];
13594 int border_element;
13596 if (!IN_LEV_FIELD(xx, yy))
13599 if (IS_PLAYER(x, y)) /* player found at center element */
13601 struct PlayerInfo *player = PLAYERINFO(x, y);
13603 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13604 border_element = Feld[xx][yy]; /* may be moving! */
13605 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13606 border_element = Feld[xx][yy];
13607 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13608 border_element = MovingOrBlocked2Element(xx, yy);
13610 continue; /* center and border element do not touch */
13612 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13613 player->index_bit, border_side);
13614 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13615 CE_PLAYER_TOUCHES_X,
13616 player->index_bit, border_side);
13618 #if USE_FIX_CE_ACTION_WITH_PLAYER
13620 /* use player element that is initially defined in the level playfield,
13621 not the player element that corresponds to the runtime player number
13622 (example: a level that contains EL_PLAYER_3 as the only player would
13623 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13624 int player_element = PLAYERINFO(x, y)->initial_element;
13626 CheckElementChangeBySide(xx, yy, border_element, player_element,
13627 CE_TOUCHING_X, border_side);
13631 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13633 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13635 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13637 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13638 continue; /* center and border element do not touch */
13641 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13642 player->index_bit, center_side);
13643 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13644 CE_PLAYER_TOUCHES_X,
13645 player->index_bit, center_side);
13647 #if USE_FIX_CE_ACTION_WITH_PLAYER
13649 /* use player element that is initially defined in the level playfield,
13650 not the player element that corresponds to the runtime player number
13651 (example: a level that contains EL_PLAYER_3 as the only player would
13652 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13653 int player_element = PLAYERINFO(xx, yy)->initial_element;
13655 CheckElementChangeBySide(x, y, center_element, player_element,
13656 CE_TOUCHING_X, center_side);
13665 #if USE_ELEMENT_TOUCHING_BUGFIX
13667 void TestIfElementTouchesCustomElement(int x, int y)
13669 static int xy[4][2] =
13676 static int trigger_sides[4][2] =
13678 /* center side border side */
13679 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13680 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13681 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13682 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13684 static int touch_dir[4] =
13686 MV_LEFT | MV_RIGHT,
13691 boolean change_center_element = FALSE;
13692 int center_element = Feld[x][y]; /* should always be non-moving! */
13693 int border_element_old[NUM_DIRECTIONS];
13696 for (i = 0; i < NUM_DIRECTIONS; i++)
13698 int xx = x + xy[i][0];
13699 int yy = y + xy[i][1];
13700 int border_element;
13702 border_element_old[i] = -1;
13704 if (!IN_LEV_FIELD(xx, yy))
13707 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13708 border_element = Feld[xx][yy]; /* may be moving! */
13709 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13710 border_element = Feld[xx][yy];
13711 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13712 border_element = MovingOrBlocked2Element(xx, yy);
13714 continue; /* center and border element do not touch */
13716 border_element_old[i] = border_element;
13719 for (i = 0; i < NUM_DIRECTIONS; i++)
13721 int xx = x + xy[i][0];
13722 int yy = y + xy[i][1];
13723 int center_side = trigger_sides[i][0];
13724 int border_element = border_element_old[i];
13726 if (border_element == -1)
13729 /* check for change of border element */
13730 CheckElementChangeBySide(xx, yy, border_element, center_element,
13731 CE_TOUCHING_X, center_side);
13733 /* (center element cannot be player, so we dont have to check this here) */
13736 for (i = 0; i < NUM_DIRECTIONS; i++)
13738 int xx = x + xy[i][0];
13739 int yy = y + xy[i][1];
13740 int border_side = trigger_sides[i][1];
13741 int border_element = border_element_old[i];
13743 if (border_element == -1)
13746 /* check for change of center element (but change it only once) */
13747 if (!change_center_element)
13748 change_center_element =
13749 CheckElementChangeBySide(x, y, center_element, border_element,
13750 CE_TOUCHING_X, border_side);
13752 #if USE_FIX_CE_ACTION_WITH_PLAYER
13753 if (IS_PLAYER(xx, yy))
13755 /* use player element that is initially defined in the level playfield,
13756 not the player element that corresponds to the runtime player number
13757 (example: a level that contains EL_PLAYER_3 as the only player would
13758 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13759 int player_element = PLAYERINFO(xx, yy)->initial_element;
13761 CheckElementChangeBySide(x, y, center_element, player_element,
13762 CE_TOUCHING_X, border_side);
13770 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13772 static int xy[4][2] =
13779 static int trigger_sides[4][2] =
13781 /* center side border side */
13782 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13783 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13784 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13785 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13787 static int touch_dir[4] =
13789 MV_LEFT | MV_RIGHT,
13794 boolean change_center_element = FALSE;
13795 int center_element = Feld[x][y]; /* should always be non-moving! */
13798 for (i = 0; i < NUM_DIRECTIONS; i++)
13800 int xx = x + xy[i][0];
13801 int yy = y + xy[i][1];
13802 int center_side = trigger_sides[i][0];
13803 int border_side = trigger_sides[i][1];
13804 int border_element;
13806 if (!IN_LEV_FIELD(xx, yy))
13809 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13810 border_element = Feld[xx][yy]; /* may be moving! */
13811 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13812 border_element = Feld[xx][yy];
13813 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13814 border_element = MovingOrBlocked2Element(xx, yy);
13816 continue; /* center and border element do not touch */
13818 /* check for change of center element (but change it only once) */
13819 if (!change_center_element)
13820 change_center_element =
13821 CheckElementChangeBySide(x, y, center_element, border_element,
13822 CE_TOUCHING_X, border_side);
13824 /* check for change of border element */
13825 CheckElementChangeBySide(xx, yy, border_element, center_element,
13826 CE_TOUCHING_X, center_side);
13832 void TestIfElementHitsCustomElement(int x, int y, int direction)
13834 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13835 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13836 int hitx = x + dx, hity = y + dy;
13837 int hitting_element = Feld[x][y];
13838 int touched_element;
13840 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13843 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13844 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13846 if (IN_LEV_FIELD(hitx, hity))
13848 int opposite_direction = MV_DIR_OPPOSITE(direction);
13849 int hitting_side = direction;
13850 int touched_side = opposite_direction;
13851 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13852 MovDir[hitx][hity] != direction ||
13853 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13859 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13860 CE_HITTING_X, touched_side);
13862 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13863 CE_HIT_BY_X, hitting_side);
13865 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13866 CE_HIT_BY_SOMETHING, opposite_direction);
13868 #if USE_FIX_CE_ACTION_WITH_PLAYER
13869 if (IS_PLAYER(hitx, hity))
13871 /* use player element that is initially defined in the level playfield,
13872 not the player element that corresponds to the runtime player number
13873 (example: a level that contains EL_PLAYER_3 as the only player would
13874 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13875 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13877 CheckElementChangeBySide(x, y, hitting_element, player_element,
13878 CE_HITTING_X, touched_side);
13884 /* "hitting something" is also true when hitting the playfield border */
13885 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13886 CE_HITTING_SOMETHING, direction);
13890 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13892 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13893 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13894 int hitx = x + dx, hity = y + dy;
13895 int hitting_element = Feld[x][y];
13896 int touched_element;
13898 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13899 !IS_FREE(hitx, hity) &&
13900 (!IS_MOVING(hitx, hity) ||
13901 MovDir[hitx][hity] != direction ||
13902 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13905 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13909 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13913 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13914 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13916 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13917 EP_CAN_SMASH_EVERYTHING, direction);
13919 if (IN_LEV_FIELD(hitx, hity))
13921 int opposite_direction = MV_DIR_OPPOSITE(direction);
13922 int hitting_side = direction;
13923 int touched_side = opposite_direction;
13925 int touched_element = MovingOrBlocked2Element(hitx, hity);
13928 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13929 MovDir[hitx][hity] != direction ||
13930 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13939 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13940 CE_SMASHED_BY_SOMETHING, opposite_direction);
13942 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13943 CE_OTHER_IS_SMASHING, touched_side);
13945 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13946 CE_OTHER_GETS_SMASHED, hitting_side);
13952 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13954 int i, kill_x = -1, kill_y = -1;
13956 int bad_element = -1;
13957 static int test_xy[4][2] =
13964 static int test_dir[4] =
13972 for (i = 0; i < NUM_DIRECTIONS; i++)
13974 int test_x, test_y, test_move_dir, test_element;
13976 test_x = good_x + test_xy[i][0];
13977 test_y = good_y + test_xy[i][1];
13979 if (!IN_LEV_FIELD(test_x, test_y))
13983 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13985 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13987 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13988 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13990 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13991 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13995 bad_element = test_element;
14001 if (kill_x != -1 || kill_y != -1)
14003 if (IS_PLAYER(good_x, good_y))
14005 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14007 if (player->shield_deadly_time_left > 0 &&
14008 !IS_INDESTRUCTIBLE(bad_element))
14009 Bang(kill_x, kill_y);
14010 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14011 KillPlayer(player);
14014 Bang(good_x, good_y);
14018 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14020 int i, kill_x = -1, kill_y = -1;
14021 int bad_element = Feld[bad_x][bad_y];
14022 static int test_xy[4][2] =
14029 static int touch_dir[4] =
14031 MV_LEFT | MV_RIGHT,
14036 static int test_dir[4] =
14044 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14047 for (i = 0; i < NUM_DIRECTIONS; i++)
14049 int test_x, test_y, test_move_dir, test_element;
14051 test_x = bad_x + test_xy[i][0];
14052 test_y = bad_y + test_xy[i][1];
14054 if (!IN_LEV_FIELD(test_x, test_y))
14058 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14060 test_element = Feld[test_x][test_y];
14062 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14063 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14065 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14066 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14068 /* good thing is player or penguin that does not move away */
14069 if (IS_PLAYER(test_x, test_y))
14071 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14073 if (bad_element == EL_ROBOT && player->is_moving)
14074 continue; /* robot does not kill player if he is moving */
14076 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14078 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14079 continue; /* center and border element do not touch */
14087 else if (test_element == EL_PENGUIN)
14097 if (kill_x != -1 || kill_y != -1)
14099 if (IS_PLAYER(kill_x, kill_y))
14101 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14103 if (player->shield_deadly_time_left > 0 &&
14104 !IS_INDESTRUCTIBLE(bad_element))
14105 Bang(bad_x, bad_y);
14106 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14107 KillPlayer(player);
14110 Bang(kill_x, kill_y);
14114 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14116 int bad_element = Feld[bad_x][bad_y];
14117 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14118 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14119 int test_x = bad_x + dx, test_y = bad_y + dy;
14120 int test_move_dir, test_element;
14121 int kill_x = -1, kill_y = -1;
14123 if (!IN_LEV_FIELD(test_x, test_y))
14127 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14129 test_element = Feld[test_x][test_y];
14131 if (test_move_dir != bad_move_dir)
14133 /* good thing can be player or penguin that does not move away */
14134 if (IS_PLAYER(test_x, test_y))
14136 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14138 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14139 player as being hit when he is moving towards the bad thing, because
14140 the "get hit by" condition would be lost after the player stops) */
14141 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14142 return; /* player moves away from bad thing */
14147 else if (test_element == EL_PENGUIN)
14154 if (kill_x != -1 || kill_y != -1)
14156 if (IS_PLAYER(kill_x, kill_y))
14158 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14160 if (player->shield_deadly_time_left > 0 &&
14161 !IS_INDESTRUCTIBLE(bad_element))
14162 Bang(bad_x, bad_y);
14163 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14164 KillPlayer(player);
14167 Bang(kill_x, kill_y);
14171 void TestIfPlayerTouchesBadThing(int x, int y)
14173 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14176 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14178 TestIfGoodThingHitsBadThing(x, y, move_dir);
14181 void TestIfBadThingTouchesPlayer(int x, int y)
14183 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14186 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14188 TestIfBadThingHitsGoodThing(x, y, move_dir);
14191 void TestIfFriendTouchesBadThing(int x, int y)
14193 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14196 void TestIfBadThingTouchesFriend(int x, int y)
14198 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14201 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14203 int i, kill_x = bad_x, kill_y = bad_y;
14204 static int xy[4][2] =
14212 for (i = 0; i < NUM_DIRECTIONS; i++)
14216 x = bad_x + xy[i][0];
14217 y = bad_y + xy[i][1];
14218 if (!IN_LEV_FIELD(x, y))
14221 element = Feld[x][y];
14222 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14223 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14231 if (kill_x != bad_x || kill_y != bad_y)
14232 Bang(bad_x, bad_y);
14235 void KillPlayer(struct PlayerInfo *player)
14237 int jx = player->jx, jy = player->jy;
14239 if (!player->active)
14242 /* the following code was introduced to prevent an infinite loop when calling
14244 -> CheckTriggeredElementChangeExt()
14245 -> ExecuteCustomElementAction()
14247 -> (infinitely repeating the above sequence of function calls)
14248 which occurs when killing the player while having a CE with the setting
14249 "kill player X when explosion of <player X>"; the solution using a new
14250 field "player->killed" was chosen for backwards compatibility, although
14251 clever use of the fields "player->active" etc. would probably also work */
14253 if (player->killed)
14257 player->killed = TRUE;
14259 /* remove accessible field at the player's position */
14260 Feld[jx][jy] = EL_EMPTY;
14262 /* deactivate shield (else Bang()/Explode() would not work right) */
14263 player->shield_normal_time_left = 0;
14264 player->shield_deadly_time_left = 0;
14268 #if USE_PLAYER_REANIMATION
14269 if (player->killed) /* player may have been reanimated */
14270 BuryPlayer(player);
14272 BuryPlayer(player);
14276 static void KillPlayerUnlessEnemyProtected(int x, int y)
14278 if (!PLAYER_ENEMY_PROTECTED(x, y))
14279 KillPlayer(PLAYERINFO(x, y));
14282 static void KillPlayerUnlessExplosionProtected(int x, int y)
14284 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14285 KillPlayer(PLAYERINFO(x, y));
14288 void BuryPlayer(struct PlayerInfo *player)
14290 int jx = player->jx, jy = player->jy;
14292 if (!player->active)
14295 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14296 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14298 player->GameOver = TRUE;
14299 RemovePlayer(player);
14302 void RemovePlayer(struct PlayerInfo *player)
14304 int jx = player->jx, jy = player->jy;
14305 int i, found = FALSE;
14307 player->present = FALSE;
14308 player->active = FALSE;
14310 if (!ExplodeField[jx][jy])
14311 StorePlayer[jx][jy] = 0;
14313 if (player->is_moving)
14314 TEST_DrawLevelField(player->last_jx, player->last_jy);
14316 for (i = 0; i < MAX_PLAYERS; i++)
14317 if (stored_player[i].active)
14321 AllPlayersGone = TRUE;
14327 #if USE_NEW_SNAP_DELAY
14328 static void setFieldForSnapping(int x, int y, int element, int direction)
14330 struct ElementInfo *ei = &element_info[element];
14331 int direction_bit = MV_DIR_TO_BIT(direction);
14332 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14333 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14334 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14336 Feld[x][y] = EL_ELEMENT_SNAPPING;
14337 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14339 ResetGfxAnimation(x, y);
14341 GfxElement[x][y] = element;
14342 GfxAction[x][y] = action;
14343 GfxDir[x][y] = direction;
14344 GfxFrame[x][y] = -1;
14349 =============================================================================
14350 checkDiagonalPushing()
14351 -----------------------------------------------------------------------------
14352 check if diagonal input device direction results in pushing of object
14353 (by checking if the alternative direction is walkable, diggable, ...)
14354 =============================================================================
14357 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14358 int x, int y, int real_dx, int real_dy)
14360 int jx, jy, dx, dy, xx, yy;
14362 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14365 /* diagonal direction: check alternative direction */
14370 xx = jx + (dx == 0 ? real_dx : 0);
14371 yy = jy + (dy == 0 ? real_dy : 0);
14373 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14377 =============================================================================
14379 -----------------------------------------------------------------------------
14380 x, y: field next to player (non-diagonal) to try to dig to
14381 real_dx, real_dy: direction as read from input device (can be diagonal)
14382 =============================================================================
14385 static int DigField(struct PlayerInfo *player,
14386 int oldx, int oldy, int x, int y,
14387 int real_dx, int real_dy, int mode)
14389 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14390 boolean player_was_pushing = player->is_pushing;
14391 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14392 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14393 int jx = oldx, jy = oldy;
14394 int dx = x - jx, dy = y - jy;
14395 int nextx = x + dx, nexty = y + dy;
14396 int move_direction = (dx == -1 ? MV_LEFT :
14397 dx == +1 ? MV_RIGHT :
14399 dy == +1 ? MV_DOWN : MV_NONE);
14400 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14401 int dig_side = MV_DIR_OPPOSITE(move_direction);
14402 int old_element = Feld[jx][jy];
14403 #if USE_FIXED_DONT_RUN_INTO
14404 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14410 if (is_player) /* function can also be called by EL_PENGUIN */
14412 if (player->MovPos == 0)
14414 player->is_digging = FALSE;
14415 player->is_collecting = FALSE;
14418 if (player->MovPos == 0) /* last pushing move finished */
14419 player->is_pushing = FALSE;
14421 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14423 player->is_switching = FALSE;
14424 player->push_delay = -1;
14426 return MP_NO_ACTION;
14430 #if !USE_FIXED_DONT_RUN_INTO
14431 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14432 return MP_NO_ACTION;
14435 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14436 old_element = Back[jx][jy];
14438 /* in case of element dropped at player position, check background */
14439 else if (Back[jx][jy] != EL_EMPTY &&
14440 game.engine_version >= VERSION_IDENT(2,2,0,0))
14441 old_element = Back[jx][jy];
14443 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14444 return MP_NO_ACTION; /* field has no opening in this direction */
14446 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14447 return MP_NO_ACTION; /* field has no opening in this direction */
14449 #if USE_FIXED_DONT_RUN_INTO
14450 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14454 Feld[jx][jy] = player->artwork_element;
14455 InitMovingField(jx, jy, MV_DOWN);
14456 Store[jx][jy] = EL_ACID;
14457 ContinueMoving(jx, jy);
14458 BuryPlayer(player);
14460 return MP_DONT_RUN_INTO;
14464 #if USE_FIXED_DONT_RUN_INTO
14465 if (player_can_move && DONT_RUN_INTO(element))
14467 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14469 return MP_DONT_RUN_INTO;
14473 #if USE_FIXED_DONT_RUN_INTO
14474 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14475 return MP_NO_ACTION;
14478 #if !USE_FIXED_DONT_RUN_INTO
14479 element = Feld[x][y];
14482 collect_count = element_info[element].collect_count_initial;
14484 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14485 return MP_NO_ACTION;
14487 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14488 player_can_move = player_can_move_or_snap;
14490 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14491 game.engine_version >= VERSION_IDENT(2,2,0,0))
14493 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14494 player->index_bit, dig_side);
14495 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14496 player->index_bit, dig_side);
14498 if (element == EL_DC_LANDMINE)
14501 if (Feld[x][y] != element) /* field changed by snapping */
14504 return MP_NO_ACTION;
14507 #if USE_PLAYER_GRAVITY
14508 if (player->gravity && is_player && !player->is_auto_moving &&
14509 canFallDown(player) && move_direction != MV_DOWN &&
14510 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14511 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14513 if (game.gravity && is_player && !player->is_auto_moving &&
14514 canFallDown(player) && move_direction != MV_DOWN &&
14515 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14516 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14519 if (player_can_move &&
14520 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14522 int sound_element = SND_ELEMENT(element);
14523 int sound_action = ACTION_WALKING;
14525 if (IS_RND_GATE(element))
14527 if (!player->key[RND_GATE_NR(element)])
14528 return MP_NO_ACTION;
14530 else if (IS_RND_GATE_GRAY(element))
14532 if (!player->key[RND_GATE_GRAY_NR(element)])
14533 return MP_NO_ACTION;
14535 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14537 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14538 return MP_NO_ACTION;
14540 else if (element == EL_EXIT_OPEN ||
14541 element == EL_EM_EXIT_OPEN ||
14542 element == EL_STEEL_EXIT_OPEN ||
14543 element == EL_EM_STEEL_EXIT_OPEN ||
14544 element == EL_SP_EXIT_OPEN ||
14545 element == EL_SP_EXIT_OPENING)
14547 sound_action = ACTION_PASSING; /* player is passing exit */
14549 else if (element == EL_EMPTY)
14551 sound_action = ACTION_MOVING; /* nothing to walk on */
14554 /* play sound from background or player, whatever is available */
14555 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14556 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14558 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14560 else if (player_can_move &&
14561 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14563 if (!ACCESS_FROM(element, opposite_direction))
14564 return MP_NO_ACTION; /* field not accessible from this direction */
14566 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14567 return MP_NO_ACTION;
14569 if (IS_EM_GATE(element))
14571 if (!player->key[EM_GATE_NR(element)])
14572 return MP_NO_ACTION;
14574 else if (IS_EM_GATE_GRAY(element))
14576 if (!player->key[EM_GATE_GRAY_NR(element)])
14577 return MP_NO_ACTION;
14579 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14581 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14582 return MP_NO_ACTION;
14584 else if (IS_EMC_GATE(element))
14586 if (!player->key[EMC_GATE_NR(element)])
14587 return MP_NO_ACTION;
14589 else if (IS_EMC_GATE_GRAY(element))
14591 if (!player->key[EMC_GATE_GRAY_NR(element)])
14592 return MP_NO_ACTION;
14594 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14596 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14597 return MP_NO_ACTION;
14599 else if (element == EL_DC_GATE_WHITE ||
14600 element == EL_DC_GATE_WHITE_GRAY ||
14601 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14603 if (player->num_white_keys == 0)
14604 return MP_NO_ACTION;
14606 player->num_white_keys--;
14608 else if (IS_SP_PORT(element))
14610 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14611 element == EL_SP_GRAVITY_PORT_RIGHT ||
14612 element == EL_SP_GRAVITY_PORT_UP ||
14613 element == EL_SP_GRAVITY_PORT_DOWN)
14614 #if USE_PLAYER_GRAVITY
14615 player->gravity = !player->gravity;
14617 game.gravity = !game.gravity;
14619 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14620 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14621 element == EL_SP_GRAVITY_ON_PORT_UP ||
14622 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14623 #if USE_PLAYER_GRAVITY
14624 player->gravity = TRUE;
14626 game.gravity = TRUE;
14628 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14629 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14630 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14631 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14632 #if USE_PLAYER_GRAVITY
14633 player->gravity = FALSE;
14635 game.gravity = FALSE;
14639 /* automatically move to the next field with double speed */
14640 player->programmed_action = move_direction;
14642 if (player->move_delay_reset_counter == 0)
14644 player->move_delay_reset_counter = 2; /* two double speed steps */
14646 DOUBLE_PLAYER_SPEED(player);
14649 PlayLevelSoundAction(x, y, ACTION_PASSING);
14651 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14655 if (mode != DF_SNAP)
14657 GfxElement[x][y] = GFX_ELEMENT(element);
14658 player->is_digging = TRUE;
14661 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14663 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14664 player->index_bit, dig_side);
14666 if (mode == DF_SNAP)
14668 #if USE_NEW_SNAP_DELAY
14669 if (level.block_snap_field)
14670 setFieldForSnapping(x, y, element, move_direction);
14672 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14674 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14677 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14678 player->index_bit, dig_side);
14681 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14685 if (is_player && mode != DF_SNAP)
14687 GfxElement[x][y] = element;
14688 player->is_collecting = TRUE;
14691 if (element == EL_SPEED_PILL)
14693 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14695 else if (element == EL_EXTRA_TIME && level.time > 0)
14697 TimeLeft += level.extra_time;
14700 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14702 DisplayGameControlValues();
14704 DrawGameValue_Time(TimeLeft);
14707 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14709 player->shield_normal_time_left += level.shield_normal_time;
14710 if (element == EL_SHIELD_DEADLY)
14711 player->shield_deadly_time_left += level.shield_deadly_time;
14713 else if (element == EL_DYNAMITE ||
14714 element == EL_EM_DYNAMITE ||
14715 element == EL_SP_DISK_RED)
14717 if (player->inventory_size < MAX_INVENTORY_SIZE)
14718 player->inventory_element[player->inventory_size++] = element;
14720 DrawGameDoorValues();
14722 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14724 player->dynabomb_count++;
14725 player->dynabombs_left++;
14727 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14729 player->dynabomb_size++;
14731 else if (element == EL_DYNABOMB_INCREASE_POWER)
14733 player->dynabomb_xl = TRUE;
14735 else if (IS_KEY(element))
14737 player->key[KEY_NR(element)] = TRUE;
14739 DrawGameDoorValues();
14741 else if (element == EL_DC_KEY_WHITE)
14743 player->num_white_keys++;
14745 /* display white keys? */
14746 /* DrawGameDoorValues(); */
14748 else if (IS_ENVELOPE(element))
14750 player->show_envelope = element;
14752 else if (element == EL_EMC_LENSES)
14754 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14756 RedrawAllInvisibleElementsForLenses();
14758 else if (element == EL_EMC_MAGNIFIER)
14760 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14762 RedrawAllInvisibleElementsForMagnifier();
14764 else if (IS_DROPPABLE(element) ||
14765 IS_THROWABLE(element)) /* can be collected and dropped */
14769 if (collect_count == 0)
14770 player->inventory_infinite_element = element;
14772 for (i = 0; i < collect_count; i++)
14773 if (player->inventory_size < MAX_INVENTORY_SIZE)
14774 player->inventory_element[player->inventory_size++] = element;
14776 DrawGameDoorValues();
14778 else if (collect_count > 0)
14780 local_player->gems_still_needed -= collect_count;
14781 if (local_player->gems_still_needed < 0)
14782 local_player->gems_still_needed = 0;
14785 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14787 DisplayGameControlValues();
14789 DrawGameValue_Emeralds(local_player->gems_still_needed);
14793 RaiseScoreElement(element);
14794 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14797 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14798 player->index_bit, dig_side);
14800 if (mode == DF_SNAP)
14802 #if USE_NEW_SNAP_DELAY
14803 if (level.block_snap_field)
14804 setFieldForSnapping(x, y, element, move_direction);
14806 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14808 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14811 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14812 player->index_bit, dig_side);
14815 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14817 if (mode == DF_SNAP && element != EL_BD_ROCK)
14818 return MP_NO_ACTION;
14820 if (CAN_FALL(element) && dy)
14821 return MP_NO_ACTION;
14823 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14824 !(element == EL_SPRING && level.use_spring_bug))
14825 return MP_NO_ACTION;
14827 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14828 ((move_direction & MV_VERTICAL &&
14829 ((element_info[element].move_pattern & MV_LEFT &&
14830 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14831 (element_info[element].move_pattern & MV_RIGHT &&
14832 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14833 (move_direction & MV_HORIZONTAL &&
14834 ((element_info[element].move_pattern & MV_UP &&
14835 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14836 (element_info[element].move_pattern & MV_DOWN &&
14837 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14838 return MP_NO_ACTION;
14840 /* do not push elements already moving away faster than player */
14841 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14842 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14843 return MP_NO_ACTION;
14845 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14847 if (player->push_delay_value == -1 || !player_was_pushing)
14848 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14850 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14852 if (player->push_delay_value == -1)
14853 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14855 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14857 if (!player->is_pushing)
14858 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14861 player->is_pushing = TRUE;
14862 player->is_active = TRUE;
14864 if (!(IN_LEV_FIELD(nextx, nexty) &&
14865 (IS_FREE(nextx, nexty) ||
14866 (IS_SB_ELEMENT(element) &&
14867 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14868 (IS_CUSTOM_ELEMENT(element) &&
14869 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14870 return MP_NO_ACTION;
14872 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14873 return MP_NO_ACTION;
14875 if (player->push_delay == -1) /* new pushing; restart delay */
14876 player->push_delay = 0;
14878 if (player->push_delay < player->push_delay_value &&
14879 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14880 element != EL_SPRING && element != EL_BALLOON)
14882 /* make sure that there is no move delay before next try to push */
14883 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14884 player->move_delay = 0;
14886 return MP_NO_ACTION;
14889 if (IS_CUSTOM_ELEMENT(element) &&
14890 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14892 if (!DigFieldByCE(nextx, nexty, element))
14893 return MP_NO_ACTION;
14896 if (IS_SB_ELEMENT(element))
14898 if (element == EL_SOKOBAN_FIELD_FULL)
14900 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14901 local_player->sokobanfields_still_needed++;
14904 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14906 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14907 local_player->sokobanfields_still_needed--;
14910 Feld[x][y] = EL_SOKOBAN_OBJECT;
14912 if (Back[x][y] == Back[nextx][nexty])
14913 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14914 else if (Back[x][y] != 0)
14915 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14918 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14921 if (local_player->sokobanfields_still_needed == 0 &&
14922 game.emulation == EMU_SOKOBAN)
14924 PlayerWins(player);
14926 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14930 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14932 InitMovingField(x, y, move_direction);
14933 GfxAction[x][y] = ACTION_PUSHING;
14935 if (mode == DF_SNAP)
14936 ContinueMoving(x, y);
14938 MovPos[x][y] = (dx != 0 ? dx : dy);
14940 Pushed[x][y] = TRUE;
14941 Pushed[nextx][nexty] = TRUE;
14943 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14944 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14946 player->push_delay_value = -1; /* get new value later */
14948 /* check for element change _after_ element has been pushed */
14949 if (game.use_change_when_pushing_bug)
14951 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14952 player->index_bit, dig_side);
14953 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14954 player->index_bit, dig_side);
14957 else if (IS_SWITCHABLE(element))
14959 if (PLAYER_SWITCHING(player, x, y))
14961 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14962 player->index_bit, dig_side);
14967 player->is_switching = TRUE;
14968 player->switch_x = x;
14969 player->switch_y = y;
14971 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14973 if (element == EL_ROBOT_WHEEL)
14975 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14979 game.robot_wheel_active = TRUE;
14981 TEST_DrawLevelField(x, y);
14983 else if (element == EL_SP_TERMINAL)
14987 SCAN_PLAYFIELD(xx, yy)
14989 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14991 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14992 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14995 else if (IS_BELT_SWITCH(element))
14997 ToggleBeltSwitch(x, y);
14999 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15000 element == EL_SWITCHGATE_SWITCH_DOWN ||
15001 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15002 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15004 ToggleSwitchgateSwitch(x, y);
15006 else if (element == EL_LIGHT_SWITCH ||
15007 element == EL_LIGHT_SWITCH_ACTIVE)
15009 ToggleLightSwitch(x, y);
15011 else if (element == EL_TIMEGATE_SWITCH ||
15012 element == EL_DC_TIMEGATE_SWITCH)
15014 ActivateTimegateSwitch(x, y);
15016 else if (element == EL_BALLOON_SWITCH_LEFT ||
15017 element == EL_BALLOON_SWITCH_RIGHT ||
15018 element == EL_BALLOON_SWITCH_UP ||
15019 element == EL_BALLOON_SWITCH_DOWN ||
15020 element == EL_BALLOON_SWITCH_NONE ||
15021 element == EL_BALLOON_SWITCH_ANY)
15023 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15024 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15025 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15026 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15027 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15030 else if (element == EL_LAMP)
15032 Feld[x][y] = EL_LAMP_ACTIVE;
15033 local_player->lights_still_needed--;
15035 ResetGfxAnimation(x, y);
15036 TEST_DrawLevelField(x, y);
15038 else if (element == EL_TIME_ORB_FULL)
15040 Feld[x][y] = EL_TIME_ORB_EMPTY;
15042 if (level.time > 0 || level.use_time_orb_bug)
15044 TimeLeft += level.time_orb_time;
15047 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15049 DisplayGameControlValues();
15051 DrawGameValue_Time(TimeLeft);
15055 ResetGfxAnimation(x, y);
15056 TEST_DrawLevelField(x, y);
15058 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15059 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15063 game.ball_state = !game.ball_state;
15065 SCAN_PLAYFIELD(xx, yy)
15067 int e = Feld[xx][yy];
15069 if (game.ball_state)
15071 if (e == EL_EMC_MAGIC_BALL)
15072 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15073 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15074 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15078 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15079 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15080 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15081 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15086 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15087 player->index_bit, dig_side);
15089 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15090 player->index_bit, dig_side);
15092 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15093 player->index_bit, dig_side);
15099 if (!PLAYER_SWITCHING(player, x, y))
15101 player->is_switching = TRUE;
15102 player->switch_x = x;
15103 player->switch_y = y;
15105 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15106 player->index_bit, dig_side);
15107 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15108 player->index_bit, dig_side);
15110 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15111 player->index_bit, dig_side);
15112 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15113 player->index_bit, dig_side);
15116 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15117 player->index_bit, dig_side);
15118 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15119 player->index_bit, dig_side);
15121 return MP_NO_ACTION;
15124 player->push_delay = -1;
15126 if (is_player) /* function can also be called by EL_PENGUIN */
15128 if (Feld[x][y] != element) /* really digged/collected something */
15130 player->is_collecting = !player->is_digging;
15131 player->is_active = TRUE;
15138 static boolean DigFieldByCE(int x, int y, int digging_element)
15140 int element = Feld[x][y];
15142 if (!IS_FREE(x, y))
15144 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15145 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15148 /* no element can dig solid indestructible elements */
15149 if (IS_INDESTRUCTIBLE(element) &&
15150 !IS_DIGGABLE(element) &&
15151 !IS_COLLECTIBLE(element))
15154 if (AmoebaNr[x][y] &&
15155 (element == EL_AMOEBA_FULL ||
15156 element == EL_BD_AMOEBA ||
15157 element == EL_AMOEBA_GROWING))
15159 AmoebaCnt[AmoebaNr[x][y]]--;
15160 AmoebaCnt2[AmoebaNr[x][y]]--;
15163 if (IS_MOVING(x, y))
15164 RemoveMovingField(x, y);
15168 TEST_DrawLevelField(x, y);
15171 /* if digged element was about to explode, prevent the explosion */
15172 ExplodeField[x][y] = EX_TYPE_NONE;
15174 PlayLevelSoundAction(x, y, action);
15177 Store[x][y] = EL_EMPTY;
15180 /* this makes it possible to leave the removed element again */
15181 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15182 Store[x][y] = element;
15184 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15186 int move_leave_element = element_info[digging_element].move_leave_element;
15188 /* this makes it possible to leave the removed element again */
15189 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15190 element : move_leave_element);
15197 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15199 int jx = player->jx, jy = player->jy;
15200 int x = jx + dx, y = jy + dy;
15201 int snap_direction = (dx == -1 ? MV_LEFT :
15202 dx == +1 ? MV_RIGHT :
15204 dy == +1 ? MV_DOWN : MV_NONE);
15205 boolean can_continue_snapping = (level.continuous_snapping &&
15206 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15208 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15211 if (!player->active || !IN_LEV_FIELD(x, y))
15219 if (player->MovPos == 0)
15220 player->is_pushing = FALSE;
15222 player->is_snapping = FALSE;
15224 if (player->MovPos == 0)
15226 player->is_moving = FALSE;
15227 player->is_digging = FALSE;
15228 player->is_collecting = FALSE;
15234 #if USE_NEW_CONTINUOUS_SNAPPING
15235 /* prevent snapping with already pressed snap key when not allowed */
15236 if (player->is_snapping && !can_continue_snapping)
15239 if (player->is_snapping)
15243 player->MovDir = snap_direction;
15245 if (player->MovPos == 0)
15247 player->is_moving = FALSE;
15248 player->is_digging = FALSE;
15249 player->is_collecting = FALSE;
15252 player->is_dropping = FALSE;
15253 player->is_dropping_pressed = FALSE;
15254 player->drop_pressed_delay = 0;
15256 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15259 player->is_snapping = TRUE;
15260 player->is_active = TRUE;
15262 if (player->MovPos == 0)
15264 player->is_moving = FALSE;
15265 player->is_digging = FALSE;
15266 player->is_collecting = FALSE;
15269 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15270 TEST_DrawLevelField(player->last_jx, player->last_jy);
15272 TEST_DrawLevelField(x, y);
15277 static boolean DropElement(struct PlayerInfo *player)
15279 int old_element, new_element;
15280 int dropx = player->jx, dropy = player->jy;
15281 int drop_direction = player->MovDir;
15282 int drop_side = drop_direction;
15284 int drop_element = get_next_dropped_element(player);
15286 int drop_element = (player->inventory_size > 0 ?
15287 player->inventory_element[player->inventory_size - 1] :
15288 player->inventory_infinite_element != EL_UNDEFINED ?
15289 player->inventory_infinite_element :
15290 player->dynabombs_left > 0 ?
15291 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15295 player->is_dropping_pressed = TRUE;
15297 /* do not drop an element on top of another element; when holding drop key
15298 pressed without moving, dropped element must move away before the next
15299 element can be dropped (this is especially important if the next element
15300 is dynamite, which can be placed on background for historical reasons) */
15301 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15304 if (IS_THROWABLE(drop_element))
15306 dropx += GET_DX_FROM_DIR(drop_direction);
15307 dropy += GET_DY_FROM_DIR(drop_direction);
15309 if (!IN_LEV_FIELD(dropx, dropy))
15313 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15314 new_element = drop_element; /* default: no change when dropping */
15316 /* check if player is active, not moving and ready to drop */
15317 if (!player->active || player->MovPos || player->drop_delay > 0)
15320 /* check if player has anything that can be dropped */
15321 if (new_element == EL_UNDEFINED)
15324 /* check if drop key was pressed long enough for EM style dynamite */
15325 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15328 /* check if anything can be dropped at the current position */
15329 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15332 /* collected custom elements can only be dropped on empty fields */
15333 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15336 if (old_element != EL_EMPTY)
15337 Back[dropx][dropy] = old_element; /* store old element on this field */
15339 ResetGfxAnimation(dropx, dropy);
15340 ResetRandomAnimationValue(dropx, dropy);
15342 if (player->inventory_size > 0 ||
15343 player->inventory_infinite_element != EL_UNDEFINED)
15345 if (player->inventory_size > 0)
15347 player->inventory_size--;
15349 DrawGameDoorValues();
15351 if (new_element == EL_DYNAMITE)
15352 new_element = EL_DYNAMITE_ACTIVE;
15353 else if (new_element == EL_EM_DYNAMITE)
15354 new_element = EL_EM_DYNAMITE_ACTIVE;
15355 else if (new_element == EL_SP_DISK_RED)
15356 new_element = EL_SP_DISK_RED_ACTIVE;
15359 Feld[dropx][dropy] = new_element;
15361 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15362 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15363 el2img(Feld[dropx][dropy]), 0);
15365 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15367 /* needed if previous element just changed to "empty" in the last frame */
15368 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15370 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15371 player->index_bit, drop_side);
15372 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15374 player->index_bit, drop_side);
15376 TestIfElementTouchesCustomElement(dropx, dropy);
15378 else /* player is dropping a dyna bomb */
15380 player->dynabombs_left--;
15382 Feld[dropx][dropy] = new_element;
15384 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15385 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15386 el2img(Feld[dropx][dropy]), 0);
15388 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15391 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15392 InitField_WithBug1(dropx, dropy, FALSE);
15394 new_element = Feld[dropx][dropy]; /* element might have changed */
15396 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15397 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15399 int move_direction, nextx, nexty;
15401 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15402 MovDir[dropx][dropy] = drop_direction;
15404 move_direction = MovDir[dropx][dropy];
15405 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15406 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15408 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15410 #if USE_FIX_IMPACT_COLLISION
15411 /* do not cause impact style collision by dropping elements that can fall */
15412 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15414 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15418 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15419 player->is_dropping = TRUE;
15421 player->drop_pressed_delay = 0;
15422 player->is_dropping_pressed = FALSE;
15424 player->drop_x = dropx;
15425 player->drop_y = dropy;
15430 /* ------------------------------------------------------------------------- */
15431 /* game sound playing functions */
15432 /* ------------------------------------------------------------------------- */
15434 static int *loop_sound_frame = NULL;
15435 static int *loop_sound_volume = NULL;
15437 void InitPlayLevelSound()
15439 int num_sounds = getSoundListSize();
15441 checked_free(loop_sound_frame);
15442 checked_free(loop_sound_volume);
15444 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15445 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15448 static void PlayLevelSound(int x, int y, int nr)
15450 int sx = SCREENX(x), sy = SCREENY(y);
15451 int volume, stereo_position;
15452 int max_distance = 8;
15453 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15455 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15456 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15459 if (!IN_LEV_FIELD(x, y) ||
15460 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15461 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15464 volume = SOUND_MAX_VOLUME;
15466 if (!IN_SCR_FIELD(sx, sy))
15468 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15469 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15471 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15474 stereo_position = (SOUND_MAX_LEFT +
15475 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15476 (SCR_FIELDX + 2 * max_distance));
15478 if (IS_LOOP_SOUND(nr))
15480 /* This assures that quieter loop sounds do not overwrite louder ones,
15481 while restarting sound volume comparison with each new game frame. */
15483 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15486 loop_sound_volume[nr] = volume;
15487 loop_sound_frame[nr] = FrameCounter;
15490 PlaySoundExt(nr, volume, stereo_position, type);
15493 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15495 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15496 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15497 y < LEVELY(BY1) ? LEVELY(BY1) :
15498 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15502 static void PlayLevelSoundAction(int x, int y, int action)
15504 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15507 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15509 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15511 if (sound_effect != SND_UNDEFINED)
15512 PlayLevelSound(x, y, sound_effect);
15515 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15518 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15520 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15521 PlayLevelSound(x, y, sound_effect);
15524 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15526 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15528 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15529 PlayLevelSound(x, y, sound_effect);
15532 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15534 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15536 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15537 StopSound(sound_effect);
15540 static void PlayLevelMusic()
15542 if (levelset.music[level_nr] != MUS_UNDEFINED)
15543 PlayMusic(levelset.music[level_nr]); /* from config file */
15545 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15548 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15550 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15551 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15552 int x = xx - 1 - offset;
15553 int y = yy - 1 - offset;
15558 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15562 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15566 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15570 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15574 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15578 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15582 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15585 case SAMPLE_android_clone:
15586 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15589 case SAMPLE_android_move:
15590 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15593 case SAMPLE_spring:
15594 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15598 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15602 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15605 case SAMPLE_eater_eat:
15606 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15610 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15613 case SAMPLE_collect:
15614 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15617 case SAMPLE_diamond:
15618 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15621 case SAMPLE_squash:
15622 /* !!! CHECK THIS !!! */
15624 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15626 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15630 case SAMPLE_wonderfall:
15631 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15635 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15639 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15643 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15647 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15651 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15655 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15658 case SAMPLE_wonder:
15659 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15663 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15666 case SAMPLE_exit_open:
15667 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15670 case SAMPLE_exit_leave:
15671 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15674 case SAMPLE_dynamite:
15675 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15679 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15683 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15687 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15691 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15695 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15699 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15703 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15709 void ChangeTime(int value)
15711 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15715 /* EMC game engine uses value from time counter of RND game engine */
15716 level.native_em_level->lev->time = *time;
15718 DrawGameValue_Time(*time);
15721 void RaiseScore(int value)
15723 /* EMC game engine and RND game engine have separate score counters */
15724 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15725 &level.native_em_level->lev->score : &local_player->score);
15729 DrawGameValue_Score(*score);
15733 void RaiseScore(int value)
15735 local_player->score += value;
15738 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15740 DisplayGameControlValues();
15742 DrawGameValue_Score(local_player->score);
15746 void RaiseScoreElement(int element)
15751 case EL_BD_DIAMOND:
15752 case EL_EMERALD_YELLOW:
15753 case EL_EMERALD_RED:
15754 case EL_EMERALD_PURPLE:
15755 case EL_SP_INFOTRON:
15756 RaiseScore(level.score[SC_EMERALD]);
15759 RaiseScore(level.score[SC_DIAMOND]);
15762 RaiseScore(level.score[SC_CRYSTAL]);
15765 RaiseScore(level.score[SC_PEARL]);
15768 case EL_BD_BUTTERFLY:
15769 case EL_SP_ELECTRON:
15770 RaiseScore(level.score[SC_BUG]);
15773 case EL_BD_FIREFLY:
15774 case EL_SP_SNIKSNAK:
15775 RaiseScore(level.score[SC_SPACESHIP]);
15778 case EL_DARK_YAMYAM:
15779 RaiseScore(level.score[SC_YAMYAM]);
15782 RaiseScore(level.score[SC_ROBOT]);
15785 RaiseScore(level.score[SC_PACMAN]);
15788 RaiseScore(level.score[SC_NUT]);
15791 case EL_EM_DYNAMITE:
15792 case EL_SP_DISK_RED:
15793 case EL_DYNABOMB_INCREASE_NUMBER:
15794 case EL_DYNABOMB_INCREASE_SIZE:
15795 case EL_DYNABOMB_INCREASE_POWER:
15796 RaiseScore(level.score[SC_DYNAMITE]);
15798 case EL_SHIELD_NORMAL:
15799 case EL_SHIELD_DEADLY:
15800 RaiseScore(level.score[SC_SHIELD]);
15802 case EL_EXTRA_TIME:
15803 RaiseScore(level.extra_time_score);
15817 case EL_DC_KEY_WHITE:
15818 RaiseScore(level.score[SC_KEY]);
15821 RaiseScore(element_info[element].collect_score);
15826 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15828 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15830 #if defined(NETWORK_AVALIABLE)
15831 if (options.network)
15832 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15841 FadeSkipNextFadeIn();
15843 fading = fading_none;
15847 OpenDoor(DOOR_CLOSE_1);
15850 game_status = GAME_MODE_MAIN;
15853 DrawAndFadeInMainMenu(REDRAW_FIELD);
15861 FadeOut(REDRAW_FIELD);
15864 game_status = GAME_MODE_MAIN;
15866 DrawAndFadeInMainMenu(REDRAW_FIELD);
15870 else /* continue playing the game */
15872 if (tape.playing && tape.deactivate_display)
15873 TapeDeactivateDisplayOff(TRUE);
15875 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15877 if (tape.playing && tape.deactivate_display)
15878 TapeDeactivateDisplayOn();
15882 void RequestQuitGame(boolean ask_if_really_quit)
15884 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15885 boolean skip_request = AllPlayersGone || quick_quit;
15887 RequestQuitGameExt(skip_request, quick_quit,
15888 "Do you really want to quit the game ?");
15892 /* ------------------------------------------------------------------------- */
15893 /* random generator functions */
15894 /* ------------------------------------------------------------------------- */
15896 unsigned int InitEngineRandom_RND(long seed)
15898 game.num_random_calls = 0;
15901 unsigned int rnd_seed = InitEngineRandom(seed);
15903 printf("::: START RND: %d\n", rnd_seed);
15908 return InitEngineRandom(seed);
15914 unsigned int RND(int max)
15918 game.num_random_calls++;
15920 return GetEngineRandom(max);
15927 /* ------------------------------------------------------------------------- */
15928 /* game engine snapshot handling functions */
15929 /* ------------------------------------------------------------------------- */
15931 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15933 struct EngineSnapshotInfo
15935 /* runtime values for custom element collect score */
15936 int collect_score[NUM_CUSTOM_ELEMENTS];
15938 /* runtime values for group element choice position */
15939 int choice_pos[NUM_GROUP_ELEMENTS];
15941 /* runtime values for belt position animations */
15942 int belt_graphic[4 * NUM_BELT_PARTS];
15943 int belt_anim_mode[4 * NUM_BELT_PARTS];
15946 struct EngineSnapshotNodeInfo
15953 static struct EngineSnapshotInfo engine_snapshot_rnd;
15954 static ListNode *engine_snapshot_list = NULL;
15955 static char *snapshot_level_identifier = NULL;
15956 static int snapshot_level_nr = -1;
15958 void FreeEngineSnapshot()
15960 while (engine_snapshot_list != NULL)
15961 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15964 setString(&snapshot_level_identifier, NULL);
15965 snapshot_level_nr = -1;
15968 static void SaveEngineSnapshotValues_RND()
15970 static int belt_base_active_element[4] =
15972 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15973 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15974 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15975 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15979 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15981 int element = EL_CUSTOM_START + i;
15983 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15986 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15988 int element = EL_GROUP_START + i;
15990 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15993 for (i = 0; i < 4; i++)
15995 for (j = 0; j < NUM_BELT_PARTS; j++)
15997 int element = belt_base_active_element[i] + j;
15998 int graphic = el2img(element);
15999 int anim_mode = graphic_info[graphic].anim_mode;
16001 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16002 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16007 static void LoadEngineSnapshotValues_RND()
16009 unsigned long num_random_calls = game.num_random_calls;
16012 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16014 int element = EL_CUSTOM_START + i;
16016 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16019 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16021 int element = EL_GROUP_START + i;
16023 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16026 for (i = 0; i < 4; i++)
16028 for (j = 0; j < NUM_BELT_PARTS; j++)
16030 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16031 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16033 graphic_info[graphic].anim_mode = anim_mode;
16037 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16039 InitRND(tape.random_seed);
16040 for (i = 0; i < num_random_calls; i++)
16044 if (game.num_random_calls != num_random_calls)
16046 Error(ERR_INFO, "number of random calls out of sync");
16047 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16048 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16049 Error(ERR_EXIT, "this should not happen -- please debug");
16053 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16055 struct EngineSnapshotNodeInfo *bi =
16056 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16058 bi->buffer_orig = buffer;
16059 bi->buffer_copy = checked_malloc(size);
16062 memcpy(bi->buffer_copy, buffer, size);
16064 addNodeToList(&engine_snapshot_list, NULL, bi);
16067 void SaveEngineSnapshot()
16069 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16071 if (level_editor_test_game) /* do not save snapshots from editor */
16074 /* copy some special values to a structure better suited for the snapshot */
16076 SaveEngineSnapshotValues_RND();
16077 SaveEngineSnapshotValues_EM();
16079 /* save values stored in special snapshot structure */
16081 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16082 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16084 /* save further RND engine values */
16086 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16087 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16088 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16090 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16091 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16092 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16093 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16095 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16096 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16097 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16098 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16099 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16101 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16102 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16103 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16105 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16107 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16109 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16110 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16112 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16113 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16114 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16115 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16116 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16117 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16118 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16119 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16120 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16121 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16122 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16123 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16124 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16125 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16126 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16127 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16128 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16129 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16131 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16132 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16134 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16135 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16136 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16138 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16139 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16141 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16142 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16143 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16144 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16145 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16147 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16148 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16150 /* save level identification information */
16152 setString(&snapshot_level_identifier, leveldir_current->identifier);
16153 snapshot_level_nr = level_nr;
16156 ListNode *node = engine_snapshot_list;
16159 while (node != NULL)
16161 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16166 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16170 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16172 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16175 void LoadEngineSnapshot()
16177 ListNode *node = engine_snapshot_list;
16179 if (engine_snapshot_list == NULL)
16182 while (node != NULL)
16184 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16189 /* restore special values from snapshot structure */
16191 LoadEngineSnapshotValues_RND();
16192 LoadEngineSnapshotValues_EM();
16195 boolean CheckEngineSnapshot()
16197 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16198 snapshot_level_nr == level_nr);
16202 /* ---------- new game button stuff ---------------------------------------- */
16204 /* graphic position values for game buttons */
16205 #define GAME_BUTTON_XSIZE 30
16206 #define GAME_BUTTON_YSIZE 30
16207 #define GAME_BUTTON_XPOS 5
16208 #define GAME_BUTTON_YPOS 215
16209 #define SOUND_BUTTON_XPOS 5
16210 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16212 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16213 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16214 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16215 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16216 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16217 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16225 } gamebutton_info[NUM_GAME_BUTTONS] =
16229 &game.button.stop.x, &game.button.stop.y,
16230 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16235 &game.button.pause.x, &game.button.pause.y,
16236 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16237 GAME_CTRL_ID_PAUSE,
16241 &game.button.play.x, &game.button.play.y,
16242 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16247 &game.button.sound_music.x, &game.button.sound_music.y,
16248 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16249 SOUND_CTRL_ID_MUSIC,
16250 "background music on/off"
16253 &game.button.sound_loops.x, &game.button.sound_loops.y,
16254 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16255 SOUND_CTRL_ID_LOOPS,
16256 "sound loops on/off"
16259 &game.button.sound_simple.x,&game.button.sound_simple.y,
16260 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16261 SOUND_CTRL_ID_SIMPLE,
16262 "normal sounds on/off"
16266 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16271 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16272 GAME_CTRL_ID_PAUSE,
16276 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16281 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16282 SOUND_CTRL_ID_MUSIC,
16283 "background music on/off"
16286 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16287 SOUND_CTRL_ID_LOOPS,
16288 "sound loops on/off"
16291 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16292 SOUND_CTRL_ID_SIMPLE,
16293 "normal sounds on/off"
16298 void CreateGameButtons()
16302 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16304 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16305 struct GadgetInfo *gi;
16308 unsigned long event_mask;
16310 int gd_xoffset, gd_yoffset;
16311 int gd_x1, gd_x2, gd_y1, gd_y2;
16314 x = DX + *gamebutton_info[i].x;
16315 y = DY + *gamebutton_info[i].y;
16316 gd_xoffset = gamebutton_info[i].gd_x;
16317 gd_yoffset = gamebutton_info[i].gd_y;
16318 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16319 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16321 if (id == GAME_CTRL_ID_STOP ||
16322 id == GAME_CTRL_ID_PAUSE ||
16323 id == GAME_CTRL_ID_PLAY)
16325 button_type = GD_TYPE_NORMAL_BUTTON;
16327 event_mask = GD_EVENT_RELEASED;
16328 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16329 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16333 button_type = GD_TYPE_CHECK_BUTTON;
16335 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16336 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16337 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16338 event_mask = GD_EVENT_PRESSED;
16339 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16340 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16343 gi = CreateGadget(GDI_CUSTOM_ID, id,
16344 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16349 GDI_X, DX + gd_xoffset,
16350 GDI_Y, DY + gd_yoffset,
16352 GDI_WIDTH, GAME_BUTTON_XSIZE,
16353 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16354 GDI_TYPE, button_type,
16355 GDI_STATE, GD_BUTTON_UNPRESSED,
16356 GDI_CHECKED, checked,
16357 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16358 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16359 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16360 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16361 GDI_DIRECT_DRAW, FALSE,
16362 GDI_EVENT_MASK, event_mask,
16363 GDI_CALLBACK_ACTION, HandleGameButtons,
16367 Error(ERR_EXIT, "cannot create gadget");
16369 game_gadget[id] = gi;
16373 void FreeGameButtons()
16377 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16378 FreeGadget(game_gadget[i]);
16381 static void MapGameButtons()
16385 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16386 MapGadget(game_gadget[i]);
16389 void UnmapGameButtons()
16393 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16394 UnmapGadget(game_gadget[i]);
16397 void RedrawGameButtons()
16401 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16402 RedrawGadget(game_gadget[i]);
16405 static void HandleGameButtons(struct GadgetInfo *gi)
16407 int id = gi->custom_id;
16409 if (game_status != GAME_MODE_PLAYING)
16414 case GAME_CTRL_ID_STOP:
16418 RequestQuitGame(TRUE);
16421 case GAME_CTRL_ID_PAUSE:
16422 if (options.network)
16424 #if defined(NETWORK_AVALIABLE)
16426 SendToServer_ContinuePlaying();
16428 SendToServer_PausePlaying();
16432 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16435 case GAME_CTRL_ID_PLAY:
16438 #if defined(NETWORK_AVALIABLE)
16439 if (options.network)
16440 SendToServer_ContinuePlaying();
16444 tape.pausing = FALSE;
16445 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16450 case SOUND_CTRL_ID_MUSIC:
16451 if (setup.sound_music)
16453 setup.sound_music = FALSE;
16456 else if (audio.music_available)
16458 setup.sound = setup.sound_music = TRUE;
16460 SetAudioMode(setup.sound);
16466 case SOUND_CTRL_ID_LOOPS:
16467 if (setup.sound_loops)
16468 setup.sound_loops = FALSE;
16469 else if (audio.loops_available)
16471 setup.sound = setup.sound_loops = TRUE;
16472 SetAudioMode(setup.sound);
16476 case SOUND_CTRL_ID_SIMPLE:
16477 if (setup.sound_simple)
16478 setup.sound_simple = FALSE;
16479 else if (audio.sound_available)
16481 setup.sound = setup.sound_simple = TRUE;
16482 SetAudioMode(setup.sound);