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];
1794 if (player->active && player->killed)
1795 player->reanimated = TRUE; /* 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_EMPTY;
3455 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
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;
3710 player->reanimated = FALSE;
3713 player->effective_action = 0;
3714 player->programmed_action = 0;
3717 player->score_final = 0;
3719 player->gems_still_needed = level.gems_needed;
3720 player->sokobanfields_still_needed = 0;
3721 player->lights_still_needed = 0;
3722 player->friends_still_needed = 0;
3724 for (j = 0; j < MAX_NUM_KEYS; j++)
3725 player->key[j] = FALSE;
3727 player->num_white_keys = 0;
3729 player->dynabomb_count = 0;
3730 player->dynabomb_size = 1;
3731 player->dynabombs_left = 0;
3732 player->dynabomb_xl = FALSE;
3734 player->MovDir = initial_move_dir;
3737 player->GfxDir = initial_move_dir;
3738 player->GfxAction = ACTION_DEFAULT;
3740 player->StepFrame = 0;
3742 player->initial_element = player->element_nr;
3743 player->artwork_element =
3744 (level.use_artwork_element[i] ? level.artwork_element[i] :
3745 player->element_nr);
3746 player->use_murphy = FALSE;
3748 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3749 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3751 player->gravity = level.initial_player_gravity[i];
3753 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3755 player->actual_frame_counter = 0;
3757 player->step_counter = 0;
3759 player->last_move_dir = initial_move_dir;
3761 player->is_active = FALSE;
3763 player->is_waiting = FALSE;
3764 player->is_moving = FALSE;
3765 player->is_auto_moving = FALSE;
3766 player->is_digging = FALSE;
3767 player->is_snapping = FALSE;
3768 player->is_collecting = FALSE;
3769 player->is_pushing = FALSE;
3770 player->is_switching = FALSE;
3771 player->is_dropping = FALSE;
3772 player->is_dropping_pressed = FALSE;
3774 player->is_bored = FALSE;
3775 player->is_sleeping = FALSE;
3777 player->frame_counter_bored = -1;
3778 player->frame_counter_sleeping = -1;
3780 player->anim_delay_counter = 0;
3781 player->post_delay_counter = 0;
3783 player->dir_waiting = initial_move_dir;
3784 player->action_waiting = ACTION_DEFAULT;
3785 player->last_action_waiting = ACTION_DEFAULT;
3786 player->special_action_bored = ACTION_DEFAULT;
3787 player->special_action_sleeping = ACTION_DEFAULT;
3789 player->switch_x = -1;
3790 player->switch_y = -1;
3792 player->drop_x = -1;
3793 player->drop_y = -1;
3795 player->show_envelope = 0;
3797 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3799 player->push_delay = -1; /* initialized when pushing starts */
3800 player->push_delay_value = game.initial_push_delay_value;
3802 player->drop_delay = 0;
3803 player->drop_pressed_delay = 0;
3805 player->last_jx = -1;
3806 player->last_jy = -1;
3810 player->shield_normal_time_left = 0;
3811 player->shield_deadly_time_left = 0;
3813 player->inventory_infinite_element = EL_UNDEFINED;
3814 player->inventory_size = 0;
3816 if (level.use_initial_inventory[i])
3818 for (j = 0; j < level.initial_inventory_size[i]; j++)
3820 int element = level.initial_inventory_content[i][j];
3821 int collect_count = element_info[element].collect_count_initial;
3824 if (!IS_CUSTOM_ELEMENT(element))
3827 if (collect_count == 0)
3828 player->inventory_infinite_element = element;
3830 for (k = 0; k < collect_count; k++)
3831 if (player->inventory_size < MAX_INVENTORY_SIZE)
3832 player->inventory_element[player->inventory_size++] = element;
3836 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3837 SnapField(player, 0, 0);
3839 player->LevelSolved = FALSE;
3840 player->GameOver = FALSE;
3842 player->LevelSolved_GameWon = FALSE;
3843 player->LevelSolved_GameEnd = FALSE;
3844 player->LevelSolved_PanelOff = FALSE;
3845 player->LevelSolved_SaveTape = FALSE;
3846 player->LevelSolved_SaveScore = FALSE;
3847 player->LevelSolved_CountingTime = 0;
3848 player->LevelSolved_CountingScore = 0;
3851 network_player_action_received = FALSE;
3853 #if defined(NETWORK_AVALIABLE)
3854 /* initial null action */
3855 if (network_playing)
3856 SendToServer_MovePlayer(MV_NONE);
3865 TimeLeft = level.time;
3868 ScreenMovDir = MV_NONE;
3872 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3874 AllPlayersGone = FALSE;
3876 game.yamyam_content_nr = 0;
3877 game.robot_wheel_active = FALSE;
3878 game.magic_wall_active = FALSE;
3879 game.magic_wall_time_left = 0;
3880 game.light_time_left = 0;
3881 game.timegate_time_left = 0;
3882 game.switchgate_pos = 0;
3883 game.wind_direction = level.wind_direction_initial;
3885 #if !USE_PLAYER_GRAVITY
3886 game.gravity = FALSE;
3887 game.explosions_delayed = TRUE;
3890 game.lenses_time_left = 0;
3891 game.magnify_time_left = 0;
3893 game.ball_state = level.ball_state_initial;
3894 game.ball_content_nr = 0;
3896 game.envelope_active = FALSE;
3898 /* set focus to local player for network games, else to all players */
3899 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3900 game.centered_player_nr_next = game.centered_player_nr;
3901 game.set_centered_player = FALSE;
3903 if (network_playing && tape.recording)
3905 /* store client dependent player focus when recording network games */
3906 tape.centered_player_nr_next = game.centered_player_nr_next;
3907 tape.set_centered_player = TRUE;
3910 for (i = 0; i < NUM_BELTS; i++)
3912 game.belt_dir[i] = MV_NONE;
3913 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3916 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3917 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3919 SCAN_PLAYFIELD(x, y)
3921 Feld[x][y] = level.field[x][y];
3922 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3923 ChangeDelay[x][y] = 0;
3924 ChangePage[x][y] = -1;
3925 #if USE_NEW_CUSTOM_VALUE
3926 CustomValue[x][y] = 0; /* initialized in InitField() */
3928 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3930 WasJustMoving[x][y] = 0;
3931 WasJustFalling[x][y] = 0;
3932 CheckCollision[x][y] = 0;
3933 CheckImpact[x][y] = 0;
3935 Pushed[x][y] = FALSE;
3937 ChangeCount[x][y] = 0;
3938 ChangeEvent[x][y] = -1;
3940 ExplodePhase[x][y] = 0;
3941 ExplodeDelay[x][y] = 0;
3942 ExplodeField[x][y] = EX_TYPE_NONE;
3944 RunnerVisit[x][y] = 0;
3945 PlayerVisit[x][y] = 0;
3948 GfxRandom[x][y] = INIT_GFX_RANDOM();
3949 GfxElement[x][y] = EL_UNDEFINED;
3950 GfxAction[x][y] = ACTION_DEFAULT;
3951 GfxDir[x][y] = MV_NONE;
3952 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3955 SCAN_PLAYFIELD(x, y)
3957 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3959 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3961 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3964 InitField(x, y, TRUE);
3966 ResetGfxAnimation(x, y);
3971 for (i = 0; i < MAX_PLAYERS; i++)
3973 struct PlayerInfo *player = &stored_player[i];
3975 /* set number of special actions for bored and sleeping animation */
3976 player->num_special_action_bored =
3977 get_num_special_action(player->artwork_element,
3978 ACTION_BORING_1, ACTION_BORING_LAST);
3979 player->num_special_action_sleeping =
3980 get_num_special_action(player->artwork_element,
3981 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3984 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3985 emulate_sb ? EMU_SOKOBAN :
3986 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3988 #if USE_NEW_ALL_SLIPPERY
3989 /* initialize type of slippery elements */
3990 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3992 if (!IS_CUSTOM_ELEMENT(i))
3994 /* default: elements slip down either to the left or right randomly */
3995 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3997 /* SP style elements prefer to slip down on the left side */
3998 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3999 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4001 /* BD style elements prefer to slip down on the left side */
4002 if (game.emulation == EMU_BOULDERDASH)
4003 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4008 /* initialize explosion and ignition delay */
4009 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4011 if (!IS_CUSTOM_ELEMENT(i))
4014 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4015 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4016 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4017 int last_phase = (num_phase + 1) * delay;
4018 int half_phase = (num_phase / 2) * delay;
4020 element_info[i].explosion_delay = last_phase - 1;
4021 element_info[i].ignition_delay = half_phase;
4023 if (i == EL_BLACK_ORB)
4024 element_info[i].ignition_delay = 1;
4028 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4029 element_info[i].explosion_delay = 1;
4031 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4032 element_info[i].ignition_delay = 1;
4036 /* correct non-moving belts to start moving left */
4037 for (i = 0; i < NUM_BELTS; i++)
4038 if (game.belt_dir[i] == MV_NONE)
4039 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4041 /* check if any connected player was not found in playfield */
4042 for (i = 0; i < MAX_PLAYERS; i++)
4044 struct PlayerInfo *player = &stored_player[i];
4046 if (player->connected && !player->present)
4048 for (j = 0; j < MAX_PLAYERS; j++)
4050 struct PlayerInfo *some_player = &stored_player[j];
4051 int jx = some_player->jx, jy = some_player->jy;
4053 /* assign first free player found that is present in the playfield */
4054 if (some_player->present && !some_player->connected)
4056 player->present = TRUE;
4057 player->active = TRUE;
4059 some_player->present = FALSE;
4060 some_player->active = FALSE;
4062 player->initial_element = some_player->initial_element;
4063 player->artwork_element = some_player->artwork_element;
4065 player->block_last_field = some_player->block_last_field;
4066 player->block_delay_adjustment = some_player->block_delay_adjustment;
4068 StorePlayer[jx][jy] = player->element_nr;
4069 player->jx = player->last_jx = jx;
4070 player->jy = player->last_jy = jy;
4080 /* when playing a tape, eliminate all players who do not participate */
4082 for (i = 0; i < MAX_PLAYERS; i++)
4084 if (stored_player[i].active && !tape.player_participates[i])
4086 struct PlayerInfo *player = &stored_player[i];
4087 int jx = player->jx, jy = player->jy;
4089 player->active = FALSE;
4090 StorePlayer[jx][jy] = 0;
4091 Feld[jx][jy] = EL_EMPTY;
4095 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4097 /* when in single player mode, eliminate all but the first active player */
4099 for (i = 0; i < MAX_PLAYERS; i++)
4101 if (stored_player[i].active)
4103 for (j = i + 1; j < MAX_PLAYERS; j++)
4105 if (stored_player[j].active)
4107 struct PlayerInfo *player = &stored_player[j];
4108 int jx = player->jx, jy = player->jy;
4110 player->active = FALSE;
4111 player->present = FALSE;
4113 StorePlayer[jx][jy] = 0;
4114 Feld[jx][jy] = EL_EMPTY;
4121 /* when recording the game, store which players take part in the game */
4124 for (i = 0; i < MAX_PLAYERS; i++)
4125 if (stored_player[i].active)
4126 tape.player_participates[i] = TRUE;
4131 for (i = 0; i < MAX_PLAYERS; i++)
4133 struct PlayerInfo *player = &stored_player[i];
4135 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4140 if (local_player == player)
4141 printf("Player %d is local player.\n", i+1);
4145 if (BorderElement == EL_EMPTY)
4148 SBX_Right = lev_fieldx - SCR_FIELDX;
4150 SBY_Lower = lev_fieldy - SCR_FIELDY;
4155 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4157 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4160 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4161 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4163 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4164 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4166 /* if local player not found, look for custom element that might create
4167 the player (make some assumptions about the right custom element) */
4168 if (!local_player->present)
4170 int start_x = 0, start_y = 0;
4171 int found_rating = 0;
4172 int found_element = EL_UNDEFINED;
4173 int player_nr = local_player->index_nr;
4175 SCAN_PLAYFIELD(x, y)
4177 int element = Feld[x][y];
4182 if (level.use_start_element[player_nr] &&
4183 level.start_element[player_nr] == element &&
4190 found_element = element;
4193 if (!IS_CUSTOM_ELEMENT(element))
4196 if (CAN_CHANGE(element))
4198 for (i = 0; i < element_info[element].num_change_pages; i++)
4200 /* check for player created from custom element as single target */
4201 content = element_info[element].change_page[i].target_element;
4202 is_player = ELEM_IS_PLAYER(content);
4204 if (is_player && (found_rating < 3 ||
4205 (found_rating == 3 && element < found_element)))
4211 found_element = element;
4216 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4218 /* check for player created from custom element as explosion content */
4219 content = element_info[element].content.e[xx][yy];
4220 is_player = ELEM_IS_PLAYER(content);
4222 if (is_player && (found_rating < 2 ||
4223 (found_rating == 2 && element < found_element)))
4225 start_x = x + xx - 1;
4226 start_y = y + yy - 1;
4229 found_element = element;
4232 if (!CAN_CHANGE(element))
4235 for (i = 0; i < element_info[element].num_change_pages; i++)
4237 /* check for player created from custom element as extended target */
4239 element_info[element].change_page[i].target_content.e[xx][yy];
4241 is_player = ELEM_IS_PLAYER(content);
4243 if (is_player && (found_rating < 1 ||
4244 (found_rating == 1 && element < found_element)))
4246 start_x = x + xx - 1;
4247 start_y = y + yy - 1;
4250 found_element = element;
4256 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4257 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4260 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4266 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4267 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268 local_player->jx - MIDPOSX);
4270 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 local_player->jy - MIDPOSY);
4276 /* do not use PLAYING mask for fading out from main screen */
4277 game_status = GAME_MODE_MAIN;
4282 if (!game.restart_level)
4283 CloseDoor(DOOR_CLOSE_1);
4286 if (level_editor_test_game)
4287 FadeSkipNextFadeIn();
4289 FadeSetEnterScreen();
4291 if (level_editor_test_game)
4292 fading = fading_none;
4294 fading = menu.destination;
4298 FadeOut(REDRAW_FIELD);
4301 FadeOut(REDRAW_FIELD);
4305 game_status = GAME_MODE_PLAYING;
4308 /* !!! FIX THIS (START) !!! */
4309 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4311 InitGameEngine_EM();
4313 /* blit playfield from scroll buffer to normal back buffer for fading in */
4314 BlitScreenToBitmap_EM(backbuffer);
4321 /* after drawing the level, correct some elements */
4322 if (game.timegate_time_left == 0)
4323 CloseAllOpenTimegates();
4325 /* blit playfield from scroll buffer to normal back buffer for fading in */
4326 if (setup.soft_scrolling)
4327 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4329 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4331 /* !!! FIX THIS (END) !!! */
4334 FadeIn(REDRAW_FIELD);
4337 FadeIn(REDRAW_FIELD);
4342 if (!game.restart_level)
4344 /* copy default game door content to main double buffer */
4345 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4346 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4349 SetPanelBackground();
4350 SetDrawBackgroundMask(REDRAW_DOOR_1);
4353 UpdateAndDisplayGameControlValues();
4355 UpdateGameDoorValues();
4356 DrawGameDoorValues();
4359 if (!game.restart_level)
4363 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4364 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4365 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4369 /* copy actual game door content to door double buffer for OpenDoor() */
4370 BlitBitmap(drawto, bitmap_db_door,
4371 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4373 OpenDoor(DOOR_OPEN_ALL);
4375 PlaySound(SND_GAME_STARTING);
4377 if (setup.sound_music)
4380 KeyboardAutoRepeatOffUnlessAutoplay();
4384 for (i = 0; i < MAX_PLAYERS; i++)
4385 printf("Player %d %sactive.\n",
4386 i + 1, (stored_player[i].active ? "" : "not "));
4397 game.restart_level = FALSE;
4400 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4402 /* this is used for non-R'n'D game engines to update certain engine values */
4404 /* needed to determine if sounds are played within the visible screen area */
4405 scroll_x = actual_scroll_x;
4406 scroll_y = actual_scroll_y;
4409 void InitMovDir(int x, int y)
4411 int i, element = Feld[x][y];
4412 static int xy[4][2] =
4419 static int direction[3][4] =
4421 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4422 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4423 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4432 Feld[x][y] = EL_BUG;
4433 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4436 case EL_SPACESHIP_RIGHT:
4437 case EL_SPACESHIP_UP:
4438 case EL_SPACESHIP_LEFT:
4439 case EL_SPACESHIP_DOWN:
4440 Feld[x][y] = EL_SPACESHIP;
4441 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4444 case EL_BD_BUTTERFLY_RIGHT:
4445 case EL_BD_BUTTERFLY_UP:
4446 case EL_BD_BUTTERFLY_LEFT:
4447 case EL_BD_BUTTERFLY_DOWN:
4448 Feld[x][y] = EL_BD_BUTTERFLY;
4449 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4452 case EL_BD_FIREFLY_RIGHT:
4453 case EL_BD_FIREFLY_UP:
4454 case EL_BD_FIREFLY_LEFT:
4455 case EL_BD_FIREFLY_DOWN:
4456 Feld[x][y] = EL_BD_FIREFLY;
4457 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4460 case EL_PACMAN_RIGHT:
4462 case EL_PACMAN_LEFT:
4463 case EL_PACMAN_DOWN:
4464 Feld[x][y] = EL_PACMAN;
4465 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4468 case EL_YAMYAM_LEFT:
4469 case EL_YAMYAM_RIGHT:
4471 case EL_YAMYAM_DOWN:
4472 Feld[x][y] = EL_YAMYAM;
4473 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4476 case EL_SP_SNIKSNAK:
4477 MovDir[x][y] = MV_UP;
4480 case EL_SP_ELECTRON:
4481 MovDir[x][y] = MV_LEFT;
4488 Feld[x][y] = EL_MOLE;
4489 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4493 if (IS_CUSTOM_ELEMENT(element))
4495 struct ElementInfo *ei = &element_info[element];
4496 int move_direction_initial = ei->move_direction_initial;
4497 int move_pattern = ei->move_pattern;
4499 if (move_direction_initial == MV_START_PREVIOUS)
4501 if (MovDir[x][y] != MV_NONE)
4504 move_direction_initial = MV_START_AUTOMATIC;
4507 if (move_direction_initial == MV_START_RANDOM)
4508 MovDir[x][y] = 1 << RND(4);
4509 else if (move_direction_initial & MV_ANY_DIRECTION)
4510 MovDir[x][y] = move_direction_initial;
4511 else if (move_pattern == MV_ALL_DIRECTIONS ||
4512 move_pattern == MV_TURNING_LEFT ||
4513 move_pattern == MV_TURNING_RIGHT ||
4514 move_pattern == MV_TURNING_LEFT_RIGHT ||
4515 move_pattern == MV_TURNING_RIGHT_LEFT ||
4516 move_pattern == MV_TURNING_RANDOM)
4517 MovDir[x][y] = 1 << RND(4);
4518 else if (move_pattern == MV_HORIZONTAL)
4519 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4520 else if (move_pattern == MV_VERTICAL)
4521 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4522 else if (move_pattern & MV_ANY_DIRECTION)
4523 MovDir[x][y] = element_info[element].move_pattern;
4524 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4525 move_pattern == MV_ALONG_RIGHT_SIDE)
4527 /* use random direction as default start direction */
4528 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4529 MovDir[x][y] = 1 << RND(4);
4531 for (i = 0; i < NUM_DIRECTIONS; i++)
4533 int x1 = x + xy[i][0];
4534 int y1 = y + xy[i][1];
4536 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4538 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4539 MovDir[x][y] = direction[0][i];
4541 MovDir[x][y] = direction[1][i];
4550 MovDir[x][y] = 1 << RND(4);
4552 if (element != EL_BUG &&
4553 element != EL_SPACESHIP &&
4554 element != EL_BD_BUTTERFLY &&
4555 element != EL_BD_FIREFLY)
4558 for (i = 0; i < NUM_DIRECTIONS; i++)
4560 int x1 = x + xy[i][0];
4561 int y1 = y + xy[i][1];
4563 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4565 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4567 MovDir[x][y] = direction[0][i];
4570 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4571 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4573 MovDir[x][y] = direction[1][i];
4582 GfxDir[x][y] = MovDir[x][y];
4585 void InitAmoebaNr(int x, int y)
4588 int group_nr = AmoebeNachbarNr(x, y);
4592 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4594 if (AmoebaCnt[i] == 0)
4602 AmoebaNr[x][y] = group_nr;
4603 AmoebaCnt[group_nr]++;
4604 AmoebaCnt2[group_nr]++;
4607 static void PlayerWins(struct PlayerInfo *player)
4609 player->LevelSolved = TRUE;
4610 player->GameOver = TRUE;
4612 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4613 level.native_em_level->lev->score : player->score);
4615 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4616 player->LevelSolved_CountingScore = player->score_final;
4621 static int time, time_final;
4622 static int score, score_final;
4623 static int game_over_delay_1 = 0;
4624 static int game_over_delay_2 = 0;
4625 int game_over_delay_value_1 = 50;
4626 int game_over_delay_value_2 = 50;
4628 if (!local_player->LevelSolved_GameWon)
4632 /* do not start end game actions before the player stops moving (to exit) */
4633 if (local_player->MovPos)
4636 local_player->LevelSolved_GameWon = TRUE;
4637 local_player->LevelSolved_SaveTape = tape.recording;
4638 local_player->LevelSolved_SaveScore = !tape.playing;
4640 if (tape.auto_play) /* tape might already be stopped here */
4641 tape.auto_play_level_solved = TRUE;
4647 game_over_delay_1 = game_over_delay_value_1;
4648 game_over_delay_2 = game_over_delay_value_2;
4650 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4651 score = score_final = local_player->score_final;
4656 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4658 else if (level.time == 0 && TimePlayed < 999)
4661 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4664 local_player->score_final = score_final;
4666 if (level_editor_test_game)
4669 score = score_final;
4672 local_player->LevelSolved_CountingTime = time;
4673 local_player->LevelSolved_CountingScore = score;
4675 game_panel_controls[GAME_PANEL_TIME].value = time;
4676 game_panel_controls[GAME_PANEL_SCORE].value = score;
4678 DisplayGameControlValues();
4680 DrawGameValue_Time(time);
4681 DrawGameValue_Score(score);
4685 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4687 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4689 /* close exit door after last player */
4690 if ((AllPlayersGone &&
4691 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4692 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4693 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4694 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4695 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4697 int element = Feld[ExitX][ExitY];
4700 if (element == EL_EM_EXIT_OPEN ||
4701 element == EL_EM_STEEL_EXIT_OPEN)
4708 Feld[ExitX][ExitY] =
4709 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4710 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4711 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4712 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4713 EL_EM_STEEL_EXIT_CLOSING);
4715 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4719 /* player disappears */
4720 DrawLevelField(ExitX, ExitY);
4723 for (i = 0; i < MAX_PLAYERS; i++)
4725 struct PlayerInfo *player = &stored_player[i];
4727 if (player->present)
4729 RemovePlayer(player);
4731 /* player disappears */
4732 DrawLevelField(player->jx, player->jy);
4737 PlaySound(SND_GAME_WINNING);
4740 if (game_over_delay_1 > 0)
4742 game_over_delay_1--;
4747 if (time != time_final)
4749 int time_to_go = ABS(time_final - time);
4750 int time_count_dir = (time < time_final ? +1 : -1);
4751 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4753 time += time_count_steps * time_count_dir;
4754 score += time_count_steps * level.score[SC_TIME_BONUS];
4757 local_player->LevelSolved_CountingTime = time;
4758 local_player->LevelSolved_CountingScore = score;
4760 game_panel_controls[GAME_PANEL_TIME].value = time;
4761 game_panel_controls[GAME_PANEL_SCORE].value = score;
4763 DisplayGameControlValues();
4765 DrawGameValue_Time(time);
4766 DrawGameValue_Score(score);
4769 if (time == time_final)
4770 StopSound(SND_GAME_LEVELTIME_BONUS);
4771 else if (setup.sound_loops)
4772 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4774 PlaySound(SND_GAME_LEVELTIME_BONUS);
4779 local_player->LevelSolved_PanelOff = TRUE;
4781 if (game_over_delay_2 > 0)
4783 game_over_delay_2--;
4796 boolean raise_level = FALSE;
4798 local_player->LevelSolved_GameEnd = TRUE;
4800 CloseDoor(DOOR_CLOSE_1);
4802 if (local_player->LevelSolved_SaveTape)
4809 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4811 SaveTape(tape.level_nr); /* ask to save tape */
4815 if (level_editor_test_game)
4817 game_status = GAME_MODE_MAIN;
4820 DrawAndFadeInMainMenu(REDRAW_FIELD);
4828 if (!local_player->LevelSolved_SaveScore)
4831 FadeOut(REDRAW_FIELD);
4834 game_status = GAME_MODE_MAIN;
4836 DrawAndFadeInMainMenu(REDRAW_FIELD);
4841 if (level_nr == leveldir_current->handicap_level)
4843 leveldir_current->handicap_level++;
4844 SaveLevelSetup_SeriesInfo();
4847 if (level_nr < leveldir_current->last_level)
4848 raise_level = TRUE; /* advance to next level */
4850 if ((hi_pos = NewHiScore()) >= 0)
4852 game_status = GAME_MODE_SCORES;
4854 DrawHallOfFame(hi_pos);
4865 FadeOut(REDRAW_FIELD);
4868 game_status = GAME_MODE_MAIN;
4876 DrawAndFadeInMainMenu(REDRAW_FIELD);
4885 LoadScore(level_nr);
4887 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4888 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4891 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4893 if (local_player->score_final > highscore[k].Score)
4895 /* player has made it to the hall of fame */
4897 if (k < MAX_SCORE_ENTRIES - 1)
4899 int m = MAX_SCORE_ENTRIES - 1;
4902 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4903 if (strEqual(setup.player_name, highscore[l].Name))
4905 if (m == k) /* player's new highscore overwrites his old one */
4909 for (l = m; l > k; l--)
4911 strcpy(highscore[l].Name, highscore[l - 1].Name);
4912 highscore[l].Score = highscore[l - 1].Score;
4919 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4920 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4921 highscore[k].Score = local_player->score_final;
4927 else if (!strncmp(setup.player_name, highscore[k].Name,
4928 MAX_PLAYER_NAME_LEN))
4929 break; /* player already there with a higher score */
4935 SaveScore(level_nr);
4940 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4942 int element = Feld[x][y];
4943 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4944 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4945 int horiz_move = (dx != 0);
4946 int sign = (horiz_move ? dx : dy);
4947 int step = sign * element_info[element].move_stepsize;
4949 /* special values for move stepsize for spring and things on conveyor belt */
4952 if (CAN_FALL(element) &&
4953 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4954 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4955 else if (element == EL_SPRING)
4956 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4962 inline static int getElementMoveStepsize(int x, int y)
4964 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4967 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4969 if (player->GfxAction != action || player->GfxDir != dir)
4972 printf("Player frame reset! (%d => %d, %d => %d)\n",
4973 player->GfxAction, action, player->GfxDir, dir);
4976 player->GfxAction = action;
4977 player->GfxDir = dir;
4979 player->StepFrame = 0;
4983 #if USE_GFX_RESET_GFX_ANIMATION
4984 static void ResetGfxFrame(int x, int y, boolean redraw)
4986 int element = Feld[x][y];
4987 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4988 int last_gfx_frame = GfxFrame[x][y];
4990 if (graphic_info[graphic].anim_global_sync)
4991 GfxFrame[x][y] = FrameCounter;
4992 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4993 GfxFrame[x][y] = CustomValue[x][y];
4994 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4995 GfxFrame[x][y] = element_info[element].collect_score;
4996 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4997 GfxFrame[x][y] = ChangeDelay[x][y];
4999 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5000 DrawLevelGraphicAnimation(x, y, graphic);
5004 static void ResetGfxAnimation(int x, int y)
5006 GfxAction[x][y] = ACTION_DEFAULT;
5007 GfxDir[x][y] = MovDir[x][y];
5010 #if USE_GFX_RESET_GFX_ANIMATION
5011 ResetGfxFrame(x, y, FALSE);
5015 static void ResetRandomAnimationValue(int x, int y)
5017 GfxRandom[x][y] = INIT_GFX_RANDOM();
5020 void InitMovingField(int x, int y, int direction)
5022 int element = Feld[x][y];
5023 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5024 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5027 boolean is_moving_before, is_moving_after;
5029 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5032 /* check if element was/is moving or being moved before/after mode change */
5035 is_moving_before = (WasJustMoving[x][y] != 0);
5037 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5038 is_moving_before = WasJustMoving[x][y];
5041 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5043 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5045 /* reset animation only for moving elements which change direction of moving
5046 or which just started or stopped moving
5047 (else CEs with property "can move" / "not moving" are reset each frame) */
5048 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5050 if (is_moving_before != is_moving_after ||
5051 direction != MovDir[x][y])
5052 ResetGfxAnimation(x, y);
5054 if ((is_moving_before || is_moving_after) && !continues_moving)
5055 ResetGfxAnimation(x, y);
5058 if (!continues_moving)
5059 ResetGfxAnimation(x, y);
5062 MovDir[x][y] = direction;
5063 GfxDir[x][y] = direction;
5065 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5066 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5067 direction == MV_DOWN && CAN_FALL(element) ?
5068 ACTION_FALLING : ACTION_MOVING);
5070 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5071 ACTION_FALLING : ACTION_MOVING);
5074 /* this is needed for CEs with property "can move" / "not moving" */
5076 if (is_moving_after)
5078 if (Feld[newx][newy] == EL_EMPTY)
5079 Feld[newx][newy] = EL_BLOCKED;
5081 MovDir[newx][newy] = MovDir[x][y];
5083 #if USE_NEW_CUSTOM_VALUE
5084 CustomValue[newx][newy] = CustomValue[x][y];
5087 GfxFrame[newx][newy] = GfxFrame[x][y];
5088 GfxRandom[newx][newy] = GfxRandom[x][y];
5089 GfxAction[newx][newy] = GfxAction[x][y];
5090 GfxDir[newx][newy] = GfxDir[x][y];
5094 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5096 int direction = MovDir[x][y];
5097 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5098 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5104 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5106 int oldx = x, oldy = y;
5107 int direction = MovDir[x][y];
5109 if (direction == MV_LEFT)
5111 else if (direction == MV_RIGHT)
5113 else if (direction == MV_UP)
5115 else if (direction == MV_DOWN)
5118 *comes_from_x = oldx;
5119 *comes_from_y = oldy;
5122 int MovingOrBlocked2Element(int x, int y)
5124 int element = Feld[x][y];
5126 if (element == EL_BLOCKED)
5130 Blocked2Moving(x, y, &oldx, &oldy);
5131 return Feld[oldx][oldy];
5137 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5139 /* like MovingOrBlocked2Element(), but if element is moving
5140 and (x,y) is the field the moving element is just leaving,
5141 return EL_BLOCKED instead of the element value */
5142 int element = Feld[x][y];
5144 if (IS_MOVING(x, y))
5146 if (element == EL_BLOCKED)
5150 Blocked2Moving(x, y, &oldx, &oldy);
5151 return Feld[oldx][oldy];
5160 static void RemoveField(int x, int y)
5162 Feld[x][y] = EL_EMPTY;
5168 #if USE_NEW_CUSTOM_VALUE
5169 CustomValue[x][y] = 0;
5173 ChangeDelay[x][y] = 0;
5174 ChangePage[x][y] = -1;
5175 Pushed[x][y] = FALSE;
5178 ExplodeField[x][y] = EX_TYPE_NONE;
5181 GfxElement[x][y] = EL_UNDEFINED;
5182 GfxAction[x][y] = ACTION_DEFAULT;
5183 GfxDir[x][y] = MV_NONE;
5185 /* !!! this would prevent the removed tile from being redrawn !!! */
5186 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5190 void RemoveMovingField(int x, int y)
5192 int oldx = x, oldy = y, newx = x, newy = y;
5193 int element = Feld[x][y];
5194 int next_element = EL_UNDEFINED;
5196 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5199 if (IS_MOVING(x, y))
5201 Moving2Blocked(x, y, &newx, &newy);
5203 if (Feld[newx][newy] != EL_BLOCKED)
5205 /* element is moving, but target field is not free (blocked), but
5206 already occupied by something different (example: acid pool);
5207 in this case, only remove the moving field, but not the target */
5209 RemoveField(oldx, oldy);
5211 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5213 TEST_DrawLevelField(oldx, oldy);
5218 else if (element == EL_BLOCKED)
5220 Blocked2Moving(x, y, &oldx, &oldy);
5221 if (!IS_MOVING(oldx, oldy))
5225 if (element == EL_BLOCKED &&
5226 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5227 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5228 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5229 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5230 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5231 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5232 next_element = get_next_element(Feld[oldx][oldy]);
5234 RemoveField(oldx, oldy);
5235 RemoveField(newx, newy);
5237 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5239 if (next_element != EL_UNDEFINED)
5240 Feld[oldx][oldy] = next_element;
5242 TEST_DrawLevelField(oldx, oldy);
5243 TEST_DrawLevelField(newx, newy);
5246 void DrawDynamite(int x, int y)
5248 int sx = SCREENX(x), sy = SCREENY(y);
5249 int graphic = el2img(Feld[x][y]);
5252 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5255 if (IS_WALKABLE_INSIDE(Back[x][y]))
5259 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5260 else if (Store[x][y])
5261 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5263 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5265 if (Back[x][y] || Store[x][y])
5266 DrawGraphicThruMask(sx, sy, graphic, frame);
5268 DrawGraphic(sx, sy, graphic, frame);
5271 void CheckDynamite(int x, int y)
5273 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5277 if (MovDelay[x][y] != 0)
5280 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5286 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5291 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5293 boolean num_checked_players = 0;
5296 for (i = 0; i < MAX_PLAYERS; i++)
5298 if (stored_player[i].active)
5300 int sx = stored_player[i].jx;
5301 int sy = stored_player[i].jy;
5303 if (num_checked_players == 0)
5310 *sx1 = MIN(*sx1, sx);
5311 *sy1 = MIN(*sy1, sy);
5312 *sx2 = MAX(*sx2, sx);
5313 *sy2 = MAX(*sy2, sy);
5316 num_checked_players++;
5321 static boolean checkIfAllPlayersFitToScreen_RND()
5323 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5325 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5327 return (sx2 - sx1 < SCR_FIELDX &&
5328 sy2 - sy1 < SCR_FIELDY);
5331 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5333 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5335 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5337 *sx = (sx1 + sx2) / 2;
5338 *sy = (sy1 + sy2) / 2;
5341 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5342 boolean center_screen, boolean quick_relocation)
5344 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5345 boolean no_delay = (tape.warp_forward);
5346 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5347 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5349 if (quick_relocation)
5351 int offset = game.scroll_delay_value;
5353 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5355 if (!level.shifted_relocation || center_screen)
5357 /* quick relocation (without scrolling), with centering of screen */
5359 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5360 x > SBX_Right + MIDPOSX ? SBX_Right :
5363 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5364 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5369 /* quick relocation (without scrolling), but do not center screen */
5371 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5372 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5375 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5376 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5379 int offset_x = x + (scroll_x - center_scroll_x);
5380 int offset_y = y + (scroll_y - center_scroll_y);
5382 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5383 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5384 offset_x - MIDPOSX);
5386 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5387 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5388 offset_y - MIDPOSY);
5393 /* quick relocation (without scrolling), inside visible screen area */
5395 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5396 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5397 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5399 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5400 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5401 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5403 /* don't scroll over playfield boundaries */
5404 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5405 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5407 /* don't scroll over playfield boundaries */
5408 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5409 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5412 RedrawPlayfield(TRUE, 0,0,0,0);
5417 int scroll_xx, scroll_yy;
5419 if (!level.shifted_relocation || center_screen)
5421 /* visible relocation (with scrolling), with centering of screen */
5423 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5424 x > SBX_Right + MIDPOSX ? SBX_Right :
5427 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5428 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5433 /* visible relocation (with scrolling), but do not center screen */
5435 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5436 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5439 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5443 int offset_x = x + (scroll_x - center_scroll_x);
5444 int offset_y = y + (scroll_y - center_scroll_y);
5446 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5447 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5448 offset_x - MIDPOSX);
5450 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5451 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5452 offset_y - MIDPOSY);
5457 /* visible relocation (with scrolling), with centering of screen */
5459 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5460 x > SBX_Right + MIDPOSX ? SBX_Right :
5463 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5464 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5468 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5470 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5473 int fx = FX, fy = FY;
5475 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5476 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5478 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5484 fx += dx * TILEX / 2;
5485 fy += dy * TILEY / 2;
5487 ScrollLevel(dx, dy);
5490 /* scroll in two steps of half tile size to make things smoother */
5491 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5493 Delay(wait_delay_value);
5495 /* scroll second step to align at full tile size */
5497 Delay(wait_delay_value);
5502 Delay(wait_delay_value);
5506 void RelocatePlayer(int jx, int jy, int el_player_raw)
5508 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5509 int player_nr = GET_PLAYER_NR(el_player);
5510 struct PlayerInfo *player = &stored_player[player_nr];
5511 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5512 boolean no_delay = (tape.warp_forward);
5513 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5514 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5515 int old_jx = player->jx;
5516 int old_jy = player->jy;
5517 int old_element = Feld[old_jx][old_jy];
5518 int element = Feld[jx][jy];
5519 boolean player_relocated = (old_jx != jx || old_jy != jy);
5521 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5522 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5523 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5524 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5525 int leave_side_horiz = move_dir_horiz;
5526 int leave_side_vert = move_dir_vert;
5527 int enter_side = enter_side_horiz | enter_side_vert;
5528 int leave_side = leave_side_horiz | leave_side_vert;
5530 if (player->GameOver) /* do not reanimate dead player */
5533 if (!player_relocated) /* no need to relocate the player */
5536 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5538 RemoveField(jx, jy); /* temporarily remove newly placed player */
5539 DrawLevelField(jx, jy);
5542 if (player->present)
5544 while (player->MovPos)
5546 ScrollPlayer(player, SCROLL_GO_ON);
5547 ScrollScreen(NULL, SCROLL_GO_ON);
5549 AdvanceFrameAndPlayerCounters(player->index_nr);
5554 Delay(wait_delay_value);
5557 DrawPlayer(player); /* needed here only to cleanup last field */
5558 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5560 player->is_moving = FALSE;
5563 if (IS_CUSTOM_ELEMENT(old_element))
5564 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5566 player->index_bit, leave_side);
5568 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5570 player->index_bit, leave_side);
5572 Feld[jx][jy] = el_player;
5573 InitPlayerField(jx, jy, el_player, TRUE);
5575 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5577 Feld[jx][jy] = element;
5578 InitField(jx, jy, FALSE);
5581 /* only visually relocate centered player */
5582 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5583 FALSE, level.instant_relocation);
5585 TestIfPlayerTouchesBadThing(jx, jy);
5586 TestIfPlayerTouchesCustomElement(jx, jy);
5588 if (IS_CUSTOM_ELEMENT(element))
5589 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5590 player->index_bit, enter_side);
5592 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5593 player->index_bit, enter_side);
5596 void Explode(int ex, int ey, int phase, int mode)
5602 /* !!! eliminate this variable !!! */
5603 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5605 if (game.explosions_delayed)
5607 ExplodeField[ex][ey] = mode;
5611 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5613 int center_element = Feld[ex][ey];
5614 int artwork_element, explosion_element; /* set these values later */
5617 /* --- This is only really needed (and now handled) in "Impact()". --- */
5618 /* do not explode moving elements that left the explode field in time */
5619 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5620 center_element == EL_EMPTY &&
5621 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5626 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5627 if (mode == EX_TYPE_NORMAL ||
5628 mode == EX_TYPE_CENTER ||
5629 mode == EX_TYPE_CROSS)
5630 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5633 /* remove things displayed in background while burning dynamite */
5634 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5637 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5639 /* put moving element to center field (and let it explode there) */
5640 center_element = MovingOrBlocked2Element(ex, ey);
5641 RemoveMovingField(ex, ey);
5642 Feld[ex][ey] = center_element;
5645 /* now "center_element" is finally determined -- set related values now */
5646 artwork_element = center_element; /* for custom player artwork */
5647 explosion_element = center_element; /* for custom player artwork */
5649 if (IS_PLAYER(ex, ey))
5651 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5653 artwork_element = stored_player[player_nr].artwork_element;
5655 if (level.use_explosion_element[player_nr])
5657 explosion_element = level.explosion_element[player_nr];
5658 artwork_element = explosion_element;
5663 if (mode == EX_TYPE_NORMAL ||
5664 mode == EX_TYPE_CENTER ||
5665 mode == EX_TYPE_CROSS)
5666 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5669 last_phase = element_info[explosion_element].explosion_delay + 1;
5671 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5673 int xx = x - ex + 1;
5674 int yy = y - ey + 1;
5677 if (!IN_LEV_FIELD(x, y) ||
5678 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5679 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5682 element = Feld[x][y];
5684 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5686 element = MovingOrBlocked2Element(x, y);
5688 if (!IS_EXPLOSION_PROOF(element))
5689 RemoveMovingField(x, y);
5692 /* indestructible elements can only explode in center (but not flames) */
5693 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5694 mode == EX_TYPE_BORDER)) ||
5695 element == EL_FLAMES)
5698 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5699 behaviour, for example when touching a yamyam that explodes to rocks
5700 with active deadly shield, a rock is created under the player !!! */
5701 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5703 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5704 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5705 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5707 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5710 if (IS_ACTIVE_BOMB(element))
5712 /* re-activate things under the bomb like gate or penguin */
5713 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5720 /* save walkable background elements while explosion on same tile */
5721 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5722 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5723 Back[x][y] = element;
5725 /* ignite explodable elements reached by other explosion */
5726 if (element == EL_EXPLOSION)
5727 element = Store2[x][y];
5729 if (AmoebaNr[x][y] &&
5730 (element == EL_AMOEBA_FULL ||
5731 element == EL_BD_AMOEBA ||
5732 element == EL_AMOEBA_GROWING))
5734 AmoebaCnt[AmoebaNr[x][y]]--;
5735 AmoebaCnt2[AmoebaNr[x][y]]--;
5740 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5742 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5744 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5746 if (PLAYERINFO(ex, ey)->use_murphy)
5747 Store[x][y] = EL_EMPTY;
5750 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5751 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5752 else if (ELEM_IS_PLAYER(center_element))
5753 Store[x][y] = EL_EMPTY;
5754 else if (center_element == EL_YAMYAM)
5755 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5756 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5757 Store[x][y] = element_info[center_element].content.e[xx][yy];
5759 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5760 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5761 otherwise) -- FIX THIS !!! */
5762 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5763 Store[x][y] = element_info[element].content.e[1][1];
5765 else if (!CAN_EXPLODE(element))
5766 Store[x][y] = element_info[element].content.e[1][1];
5769 Store[x][y] = EL_EMPTY;
5771 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5772 center_element == EL_AMOEBA_TO_DIAMOND)
5773 Store2[x][y] = element;
5775 Feld[x][y] = EL_EXPLOSION;
5776 GfxElement[x][y] = artwork_element;
5778 ExplodePhase[x][y] = 1;
5779 ExplodeDelay[x][y] = last_phase;
5784 if (center_element == EL_YAMYAM)
5785 game.yamyam_content_nr =
5786 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5798 GfxFrame[x][y] = 0; /* restart explosion animation */
5800 last_phase = ExplodeDelay[x][y];
5802 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5806 /* activate this even in non-DEBUG version until cause for crash in
5807 getGraphicAnimationFrame() (see below) is found and eliminated */
5813 /* this can happen if the player leaves an explosion just in time */
5814 if (GfxElement[x][y] == EL_UNDEFINED)
5815 GfxElement[x][y] = EL_EMPTY;
5817 if (GfxElement[x][y] == EL_UNDEFINED)
5820 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5821 printf("Explode(): This should never happen!\n");
5824 GfxElement[x][y] = EL_EMPTY;
5830 border_element = Store2[x][y];
5831 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5832 border_element = StorePlayer[x][y];
5834 if (phase == element_info[border_element].ignition_delay ||
5835 phase == last_phase)
5837 boolean border_explosion = FALSE;
5839 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5840 !PLAYER_EXPLOSION_PROTECTED(x, y))
5842 KillPlayerUnlessExplosionProtected(x, y);
5843 border_explosion = TRUE;
5845 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5847 Feld[x][y] = Store2[x][y];
5850 border_explosion = TRUE;
5852 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5854 AmoebeUmwandeln(x, y);
5856 border_explosion = TRUE;
5859 /* if an element just explodes due to another explosion (chain-reaction),
5860 do not immediately end the new explosion when it was the last frame of
5861 the explosion (as it would be done in the following "if"-statement!) */
5862 if (border_explosion && phase == last_phase)
5866 if (phase == last_phase)
5870 element = Feld[x][y] = Store[x][y];
5871 Store[x][y] = Store2[x][y] = 0;
5872 GfxElement[x][y] = EL_UNDEFINED;
5874 /* player can escape from explosions and might therefore be still alive */
5875 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5876 element <= EL_PLAYER_IS_EXPLODING_4)
5878 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5879 int explosion_element = EL_PLAYER_1 + player_nr;
5880 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5881 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5883 if (level.use_explosion_element[player_nr])
5884 explosion_element = level.explosion_element[player_nr];
5886 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5887 element_info[explosion_element].content.e[xx][yy]);
5890 /* restore probably existing indestructible background element */
5891 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5892 element = Feld[x][y] = Back[x][y];
5895 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5896 GfxDir[x][y] = MV_NONE;
5897 ChangeDelay[x][y] = 0;
5898 ChangePage[x][y] = -1;
5900 #if USE_NEW_CUSTOM_VALUE
5901 CustomValue[x][y] = 0;
5904 InitField_WithBug2(x, y, FALSE);
5906 TEST_DrawLevelField(x, y);
5908 TestIfElementTouchesCustomElement(x, y);
5910 if (GFX_CRUMBLED(element))
5911 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5913 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5914 StorePlayer[x][y] = 0;
5916 if (ELEM_IS_PLAYER(element))
5917 RelocatePlayer(x, y, element);
5919 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5921 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5922 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5925 TEST_DrawLevelFieldCrumbledSand(x, y);
5927 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5929 DrawLevelElement(x, y, Back[x][y]);
5930 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5932 else if (IS_WALKABLE_UNDER(Back[x][y]))
5934 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5935 DrawLevelElementThruMask(x, y, Back[x][y]);
5937 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5938 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5942 void DynaExplode(int ex, int ey)
5945 int dynabomb_element = Feld[ex][ey];
5946 int dynabomb_size = 1;
5947 boolean dynabomb_xl = FALSE;
5948 struct PlayerInfo *player;
5949 static int xy[4][2] =
5957 if (IS_ACTIVE_BOMB(dynabomb_element))
5959 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5960 dynabomb_size = player->dynabomb_size;
5961 dynabomb_xl = player->dynabomb_xl;
5962 player->dynabombs_left++;
5965 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5967 for (i = 0; i < NUM_DIRECTIONS; i++)
5969 for (j = 1; j <= dynabomb_size; j++)
5971 int x = ex + j * xy[i][0];
5972 int y = ey + j * xy[i][1];
5975 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5978 element = Feld[x][y];
5980 /* do not restart explosions of fields with active bombs */
5981 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5984 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5986 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5987 !IS_DIGGABLE(element) && !dynabomb_xl)
5993 void Bang(int x, int y)
5995 int element = MovingOrBlocked2Element(x, y);
5996 int explosion_type = EX_TYPE_NORMAL;
5998 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6000 struct PlayerInfo *player = PLAYERINFO(x, y);
6002 #if USE_FIX_CE_ACTION_WITH_PLAYER
6003 element = Feld[x][y] = player->initial_element;
6005 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6006 player->element_nr);
6009 if (level.use_explosion_element[player->index_nr])
6011 int explosion_element = level.explosion_element[player->index_nr];
6013 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6014 explosion_type = EX_TYPE_CROSS;
6015 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6016 explosion_type = EX_TYPE_CENTER;
6024 case EL_BD_BUTTERFLY:
6027 case EL_DARK_YAMYAM:
6031 RaiseScoreElement(element);
6034 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6035 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6036 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6037 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6038 case EL_DYNABOMB_INCREASE_NUMBER:
6039 case EL_DYNABOMB_INCREASE_SIZE:
6040 case EL_DYNABOMB_INCREASE_POWER:
6041 explosion_type = EX_TYPE_DYNA;
6044 case EL_DC_LANDMINE:
6046 case EL_EM_EXIT_OPEN:
6047 case EL_EM_STEEL_EXIT_OPEN:
6049 explosion_type = EX_TYPE_CENTER;
6054 case EL_LAMP_ACTIVE:
6055 case EL_AMOEBA_TO_DIAMOND:
6056 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6057 explosion_type = EX_TYPE_CENTER;
6061 if (element_info[element].explosion_type == EXPLODES_CROSS)
6062 explosion_type = EX_TYPE_CROSS;
6063 else if (element_info[element].explosion_type == EXPLODES_1X1)
6064 explosion_type = EX_TYPE_CENTER;
6068 if (explosion_type == EX_TYPE_DYNA)
6071 Explode(x, y, EX_PHASE_START, explosion_type);
6073 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6076 void SplashAcid(int x, int y)
6078 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6079 (!IN_LEV_FIELD(x - 1, y - 2) ||
6080 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6081 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6083 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6084 (!IN_LEV_FIELD(x + 1, y - 2) ||
6085 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6086 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6088 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6091 static void InitBeltMovement()
6093 static int belt_base_element[4] =
6095 EL_CONVEYOR_BELT_1_LEFT,
6096 EL_CONVEYOR_BELT_2_LEFT,
6097 EL_CONVEYOR_BELT_3_LEFT,
6098 EL_CONVEYOR_BELT_4_LEFT
6100 static int belt_base_active_element[4] =
6102 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6103 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6104 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6105 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6110 /* set frame order for belt animation graphic according to belt direction */
6111 for (i = 0; i < NUM_BELTS; i++)
6115 for (j = 0; j < NUM_BELT_PARTS; j++)
6117 int element = belt_base_active_element[belt_nr] + j;
6118 int graphic_1 = el2img(element);
6119 int graphic_2 = el2panelimg(element);
6121 if (game.belt_dir[i] == MV_LEFT)
6123 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6124 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6128 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6129 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6134 SCAN_PLAYFIELD(x, y)
6136 int element = Feld[x][y];
6138 for (i = 0; i < NUM_BELTS; i++)
6140 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6142 int e_belt_nr = getBeltNrFromBeltElement(element);
6145 if (e_belt_nr == belt_nr)
6147 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6149 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6156 static void ToggleBeltSwitch(int x, int y)
6158 static int belt_base_element[4] =
6160 EL_CONVEYOR_BELT_1_LEFT,
6161 EL_CONVEYOR_BELT_2_LEFT,
6162 EL_CONVEYOR_BELT_3_LEFT,
6163 EL_CONVEYOR_BELT_4_LEFT
6165 static int belt_base_active_element[4] =
6167 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6168 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6169 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6170 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6172 static int belt_base_switch_element[4] =
6174 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6175 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6176 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6177 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6179 static int belt_move_dir[4] =
6187 int element = Feld[x][y];
6188 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6189 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6190 int belt_dir = belt_move_dir[belt_dir_nr];
6193 if (!IS_BELT_SWITCH(element))
6196 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6197 game.belt_dir[belt_nr] = belt_dir;
6199 if (belt_dir_nr == 3)
6202 /* set frame order for belt animation graphic according to belt direction */
6203 for (i = 0; i < NUM_BELT_PARTS; i++)
6205 int element = belt_base_active_element[belt_nr] + i;
6206 int graphic_1 = el2img(element);
6207 int graphic_2 = el2panelimg(element);
6209 if (belt_dir == MV_LEFT)
6211 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6212 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6216 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6217 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6221 SCAN_PLAYFIELD(xx, yy)
6223 int element = Feld[xx][yy];
6225 if (IS_BELT_SWITCH(element))
6227 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6229 if (e_belt_nr == belt_nr)
6231 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6232 TEST_DrawLevelField(xx, yy);
6235 else if (IS_BELT(element) && belt_dir != MV_NONE)
6237 int e_belt_nr = getBeltNrFromBeltElement(element);
6239 if (e_belt_nr == belt_nr)
6241 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6243 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6244 TEST_DrawLevelField(xx, yy);
6247 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6249 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6251 if (e_belt_nr == belt_nr)
6253 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6255 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6256 TEST_DrawLevelField(xx, yy);
6262 static void ToggleSwitchgateSwitch(int x, int y)
6266 game.switchgate_pos = !game.switchgate_pos;
6268 SCAN_PLAYFIELD(xx, yy)
6270 int element = Feld[xx][yy];
6272 #if !USE_BOTH_SWITCHGATE_SWITCHES
6273 if (element == EL_SWITCHGATE_SWITCH_UP ||
6274 element == EL_SWITCHGATE_SWITCH_DOWN)
6276 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6277 TEST_DrawLevelField(xx, yy);
6279 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6280 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6282 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6283 TEST_DrawLevelField(xx, yy);
6286 if (element == EL_SWITCHGATE_SWITCH_UP)
6288 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6289 TEST_DrawLevelField(xx, yy);
6291 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6293 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6294 TEST_DrawLevelField(xx, yy);
6296 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6298 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6299 TEST_DrawLevelField(xx, yy);
6301 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6303 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6304 TEST_DrawLevelField(xx, yy);
6307 else if (element == EL_SWITCHGATE_OPEN ||
6308 element == EL_SWITCHGATE_OPENING)
6310 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6312 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6314 else if (element == EL_SWITCHGATE_CLOSED ||
6315 element == EL_SWITCHGATE_CLOSING)
6317 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6319 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6324 static int getInvisibleActiveFromInvisibleElement(int element)
6326 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6327 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6328 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6332 static int getInvisibleFromInvisibleActiveElement(int element)
6334 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6335 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6336 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6340 static void RedrawAllLightSwitchesAndInvisibleElements()
6344 SCAN_PLAYFIELD(x, y)
6346 int element = Feld[x][y];
6348 if (element == EL_LIGHT_SWITCH &&
6349 game.light_time_left > 0)
6351 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6352 TEST_DrawLevelField(x, y);
6354 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6355 game.light_time_left == 0)
6357 Feld[x][y] = EL_LIGHT_SWITCH;
6358 TEST_DrawLevelField(x, y);
6360 else if (element == EL_EMC_DRIPPER &&
6361 game.light_time_left > 0)
6363 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6364 TEST_DrawLevelField(x, y);
6366 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6367 game.light_time_left == 0)
6369 Feld[x][y] = EL_EMC_DRIPPER;
6370 TEST_DrawLevelField(x, y);
6372 else if (element == EL_INVISIBLE_STEELWALL ||
6373 element == EL_INVISIBLE_WALL ||
6374 element == EL_INVISIBLE_SAND)
6376 if (game.light_time_left > 0)
6377 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6379 TEST_DrawLevelField(x, y);
6381 /* uncrumble neighbour fields, if needed */
6382 if (element == EL_INVISIBLE_SAND)
6383 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6385 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6386 element == EL_INVISIBLE_WALL_ACTIVE ||
6387 element == EL_INVISIBLE_SAND_ACTIVE)
6389 if (game.light_time_left == 0)
6390 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6392 TEST_DrawLevelField(x, y);
6394 /* re-crumble neighbour fields, if needed */
6395 if (element == EL_INVISIBLE_SAND)
6396 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6401 static void RedrawAllInvisibleElementsForLenses()
6405 SCAN_PLAYFIELD(x, y)
6407 int element = Feld[x][y];
6409 if (element == EL_EMC_DRIPPER &&
6410 game.lenses_time_left > 0)
6412 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6413 TEST_DrawLevelField(x, y);
6415 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6416 game.lenses_time_left == 0)
6418 Feld[x][y] = EL_EMC_DRIPPER;
6419 TEST_DrawLevelField(x, y);
6421 else if (element == EL_INVISIBLE_STEELWALL ||
6422 element == EL_INVISIBLE_WALL ||
6423 element == EL_INVISIBLE_SAND)
6425 if (game.lenses_time_left > 0)
6426 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6428 TEST_DrawLevelField(x, y);
6430 /* uncrumble neighbour fields, if needed */
6431 if (element == EL_INVISIBLE_SAND)
6432 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6434 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6435 element == EL_INVISIBLE_WALL_ACTIVE ||
6436 element == EL_INVISIBLE_SAND_ACTIVE)
6438 if (game.lenses_time_left == 0)
6439 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6441 TEST_DrawLevelField(x, y);
6443 /* re-crumble neighbour fields, if needed */
6444 if (element == EL_INVISIBLE_SAND)
6445 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6450 static void RedrawAllInvisibleElementsForMagnifier()
6454 SCAN_PLAYFIELD(x, y)
6456 int element = Feld[x][y];
6458 if (element == EL_EMC_FAKE_GRASS &&
6459 game.magnify_time_left > 0)
6461 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6462 TEST_DrawLevelField(x, y);
6464 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6465 game.magnify_time_left == 0)
6467 Feld[x][y] = EL_EMC_FAKE_GRASS;
6468 TEST_DrawLevelField(x, y);
6470 else if (IS_GATE_GRAY(element) &&
6471 game.magnify_time_left > 0)
6473 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6474 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6475 IS_EM_GATE_GRAY(element) ?
6476 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6477 IS_EMC_GATE_GRAY(element) ?
6478 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6479 IS_DC_GATE_GRAY(element) ?
6480 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6482 TEST_DrawLevelField(x, y);
6484 else if (IS_GATE_GRAY_ACTIVE(element) &&
6485 game.magnify_time_left == 0)
6487 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6488 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6489 IS_EM_GATE_GRAY_ACTIVE(element) ?
6490 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6491 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6492 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6493 IS_DC_GATE_GRAY_ACTIVE(element) ?
6494 EL_DC_GATE_WHITE_GRAY :
6496 TEST_DrawLevelField(x, y);
6501 static void ToggleLightSwitch(int x, int y)
6503 int element = Feld[x][y];
6505 game.light_time_left =
6506 (element == EL_LIGHT_SWITCH ?
6507 level.time_light * FRAMES_PER_SECOND : 0);
6509 RedrawAllLightSwitchesAndInvisibleElements();
6512 static void ActivateTimegateSwitch(int x, int y)
6516 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6518 SCAN_PLAYFIELD(xx, yy)
6520 int element = Feld[xx][yy];
6522 if (element == EL_TIMEGATE_CLOSED ||
6523 element == EL_TIMEGATE_CLOSING)
6525 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6526 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6530 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6532 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6533 TEST_DrawLevelField(xx, yy);
6540 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6541 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6543 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6547 void Impact(int x, int y)
6549 boolean last_line = (y == lev_fieldy - 1);
6550 boolean object_hit = FALSE;
6551 boolean impact = (last_line || object_hit);
6552 int element = Feld[x][y];
6553 int smashed = EL_STEELWALL;
6555 if (!last_line) /* check if element below was hit */
6557 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6560 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6561 MovDir[x][y + 1] != MV_DOWN ||
6562 MovPos[x][y + 1] <= TILEY / 2));
6564 /* do not smash moving elements that left the smashed field in time */
6565 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6566 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6569 #if USE_QUICKSAND_IMPACT_BUGFIX
6570 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6572 RemoveMovingField(x, y + 1);
6573 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6574 Feld[x][y + 2] = EL_ROCK;
6575 TEST_DrawLevelField(x, y + 2);
6580 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6582 RemoveMovingField(x, y + 1);
6583 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6584 Feld[x][y + 2] = EL_ROCK;
6585 TEST_DrawLevelField(x, y + 2);
6592 smashed = MovingOrBlocked2Element(x, y + 1);
6594 impact = (last_line || object_hit);
6597 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6599 SplashAcid(x, y + 1);
6603 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6604 /* only reset graphic animation if graphic really changes after impact */
6606 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6608 ResetGfxAnimation(x, y);
6609 TEST_DrawLevelField(x, y);
6612 if (impact && CAN_EXPLODE_IMPACT(element))
6617 else if (impact && element == EL_PEARL &&
6618 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6620 ResetGfxAnimation(x, y);
6622 Feld[x][y] = EL_PEARL_BREAKING;
6623 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6626 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6628 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6633 if (impact && element == EL_AMOEBA_DROP)
6635 if (object_hit && IS_PLAYER(x, y + 1))
6636 KillPlayerUnlessEnemyProtected(x, y + 1);
6637 else if (object_hit && smashed == EL_PENGUIN)
6641 Feld[x][y] = EL_AMOEBA_GROWING;
6642 Store[x][y] = EL_AMOEBA_WET;
6644 ResetRandomAnimationValue(x, y);
6649 if (object_hit) /* check which object was hit */
6651 if ((CAN_PASS_MAGIC_WALL(element) &&
6652 (smashed == EL_MAGIC_WALL ||
6653 smashed == EL_BD_MAGIC_WALL)) ||
6654 (CAN_PASS_DC_MAGIC_WALL(element) &&
6655 smashed == EL_DC_MAGIC_WALL))
6658 int activated_magic_wall =
6659 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6660 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6661 EL_DC_MAGIC_WALL_ACTIVE);
6663 /* activate magic wall / mill */
6664 SCAN_PLAYFIELD(xx, yy)
6666 if (Feld[xx][yy] == smashed)
6667 Feld[xx][yy] = activated_magic_wall;
6670 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6671 game.magic_wall_active = TRUE;
6673 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6674 SND_MAGIC_WALL_ACTIVATING :
6675 smashed == EL_BD_MAGIC_WALL ?
6676 SND_BD_MAGIC_WALL_ACTIVATING :
6677 SND_DC_MAGIC_WALL_ACTIVATING));
6680 if (IS_PLAYER(x, y + 1))
6682 if (CAN_SMASH_PLAYER(element))
6684 KillPlayerUnlessEnemyProtected(x, y + 1);
6688 else if (smashed == EL_PENGUIN)
6690 if (CAN_SMASH_PLAYER(element))
6696 else if (element == EL_BD_DIAMOND)
6698 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6704 else if (((element == EL_SP_INFOTRON ||
6705 element == EL_SP_ZONK) &&
6706 (smashed == EL_SP_SNIKSNAK ||
6707 smashed == EL_SP_ELECTRON ||
6708 smashed == EL_SP_DISK_ORANGE)) ||
6709 (element == EL_SP_INFOTRON &&
6710 smashed == EL_SP_DISK_YELLOW))
6715 else if (CAN_SMASH_EVERYTHING(element))
6717 if (IS_CLASSIC_ENEMY(smashed) ||
6718 CAN_EXPLODE_SMASHED(smashed))
6723 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6725 if (smashed == EL_LAMP ||
6726 smashed == EL_LAMP_ACTIVE)
6731 else if (smashed == EL_NUT)
6733 Feld[x][y + 1] = EL_NUT_BREAKING;
6734 PlayLevelSound(x, y, SND_NUT_BREAKING);
6735 RaiseScoreElement(EL_NUT);
6738 else if (smashed == EL_PEARL)
6740 ResetGfxAnimation(x, y);
6742 Feld[x][y + 1] = EL_PEARL_BREAKING;
6743 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6746 else if (smashed == EL_DIAMOND)
6748 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6749 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6752 else if (IS_BELT_SWITCH(smashed))
6754 ToggleBeltSwitch(x, y + 1);
6756 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6757 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6758 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6759 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6761 ToggleSwitchgateSwitch(x, y + 1);
6763 else if (smashed == EL_LIGHT_SWITCH ||
6764 smashed == EL_LIGHT_SWITCH_ACTIVE)
6766 ToggleLightSwitch(x, y + 1);
6771 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6774 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6776 CheckElementChangeBySide(x, y + 1, smashed, element,
6777 CE_SWITCHED, CH_SIDE_TOP);
6778 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6784 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6789 /* play sound of magic wall / mill */
6791 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6792 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6793 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6795 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6796 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6797 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6798 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6799 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6800 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6805 /* play sound of object that hits the ground */
6806 if (last_line || object_hit)
6807 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6810 inline static void TurnRoundExt(int x, int y)
6822 { 0, 0 }, { 0, 0 }, { 0, 0 },
6827 int left, right, back;
6831 { MV_DOWN, MV_UP, MV_RIGHT },
6832 { MV_UP, MV_DOWN, MV_LEFT },
6834 { MV_LEFT, MV_RIGHT, MV_DOWN },
6838 { MV_RIGHT, MV_LEFT, MV_UP }
6841 int element = Feld[x][y];
6842 int move_pattern = element_info[element].move_pattern;
6844 int old_move_dir = MovDir[x][y];
6845 int left_dir = turn[old_move_dir].left;
6846 int right_dir = turn[old_move_dir].right;
6847 int back_dir = turn[old_move_dir].back;
6849 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6850 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6851 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6852 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6854 int left_x = x + left_dx, left_y = y + left_dy;
6855 int right_x = x + right_dx, right_y = y + right_dy;
6856 int move_x = x + move_dx, move_y = y + move_dy;
6860 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6862 TestIfBadThingTouchesOtherBadThing(x, y);
6864 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6865 MovDir[x][y] = right_dir;
6866 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6867 MovDir[x][y] = left_dir;
6869 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6871 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6874 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6876 TestIfBadThingTouchesOtherBadThing(x, y);
6878 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6879 MovDir[x][y] = left_dir;
6880 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6881 MovDir[x][y] = right_dir;
6883 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6885 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6888 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6890 TestIfBadThingTouchesOtherBadThing(x, y);
6892 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6893 MovDir[x][y] = left_dir;
6894 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6895 MovDir[x][y] = right_dir;
6897 if (MovDir[x][y] != old_move_dir)
6900 else if (element == EL_YAMYAM)
6902 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6903 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6905 if (can_turn_left && can_turn_right)
6906 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6907 else if (can_turn_left)
6908 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6909 else if (can_turn_right)
6910 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6912 MovDir[x][y] = back_dir;
6914 MovDelay[x][y] = 16 + 16 * RND(3);
6916 else if (element == EL_DARK_YAMYAM)
6918 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6920 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6923 if (can_turn_left && can_turn_right)
6924 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6925 else if (can_turn_left)
6926 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6927 else if (can_turn_right)
6928 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6930 MovDir[x][y] = back_dir;
6932 MovDelay[x][y] = 16 + 16 * RND(3);
6934 else if (element == EL_PACMAN)
6936 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6937 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6939 if (can_turn_left && can_turn_right)
6940 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6941 else if (can_turn_left)
6942 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6943 else if (can_turn_right)
6944 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6946 MovDir[x][y] = back_dir;
6948 MovDelay[x][y] = 6 + RND(40);
6950 else if (element == EL_PIG)
6952 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6953 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6954 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6955 boolean should_turn_left, should_turn_right, should_move_on;
6957 int rnd = RND(rnd_value);
6959 should_turn_left = (can_turn_left &&
6961 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6962 y + back_dy + left_dy)));
6963 should_turn_right = (can_turn_right &&
6965 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6966 y + back_dy + right_dy)));
6967 should_move_on = (can_move_on &&
6970 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6971 y + move_dy + left_dy) ||
6972 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6973 y + move_dy + right_dy)));
6975 if (should_turn_left || should_turn_right || should_move_on)
6977 if (should_turn_left && should_turn_right && should_move_on)
6978 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6979 rnd < 2 * rnd_value / 3 ? right_dir :
6981 else if (should_turn_left && should_turn_right)
6982 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6983 else if (should_turn_left && should_move_on)
6984 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6985 else if (should_turn_right && should_move_on)
6986 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6987 else if (should_turn_left)
6988 MovDir[x][y] = left_dir;
6989 else if (should_turn_right)
6990 MovDir[x][y] = right_dir;
6991 else if (should_move_on)
6992 MovDir[x][y] = old_move_dir;
6994 else if (can_move_on && rnd > rnd_value / 8)
6995 MovDir[x][y] = old_move_dir;
6996 else if (can_turn_left && can_turn_right)
6997 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6998 else if (can_turn_left && rnd > rnd_value / 8)
6999 MovDir[x][y] = left_dir;
7000 else if (can_turn_right && rnd > rnd_value/8)
7001 MovDir[x][y] = right_dir;
7003 MovDir[x][y] = back_dir;
7005 xx = x + move_xy[MovDir[x][y]].dx;
7006 yy = y + move_xy[MovDir[x][y]].dy;
7008 if (!IN_LEV_FIELD(xx, yy) ||
7009 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7010 MovDir[x][y] = old_move_dir;
7014 else if (element == EL_DRAGON)
7016 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7017 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7018 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7020 int rnd = RND(rnd_value);
7022 if (can_move_on && rnd > rnd_value / 8)
7023 MovDir[x][y] = old_move_dir;
7024 else if (can_turn_left && can_turn_right)
7025 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7026 else if (can_turn_left && rnd > rnd_value / 8)
7027 MovDir[x][y] = left_dir;
7028 else if (can_turn_right && rnd > rnd_value / 8)
7029 MovDir[x][y] = right_dir;
7031 MovDir[x][y] = back_dir;
7033 xx = x + move_xy[MovDir[x][y]].dx;
7034 yy = y + move_xy[MovDir[x][y]].dy;
7036 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7037 MovDir[x][y] = old_move_dir;
7041 else if (element == EL_MOLE)
7043 boolean can_move_on =
7044 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7045 IS_AMOEBOID(Feld[move_x][move_y]) ||
7046 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7049 boolean can_turn_left =
7050 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7051 IS_AMOEBOID(Feld[left_x][left_y])));
7053 boolean can_turn_right =
7054 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7055 IS_AMOEBOID(Feld[right_x][right_y])));
7057 if (can_turn_left && can_turn_right)
7058 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7059 else if (can_turn_left)
7060 MovDir[x][y] = left_dir;
7062 MovDir[x][y] = right_dir;
7065 if (MovDir[x][y] != old_move_dir)
7068 else if (element == EL_BALLOON)
7070 MovDir[x][y] = game.wind_direction;
7073 else if (element == EL_SPRING)
7075 #if USE_NEW_SPRING_BUMPER
7076 if (MovDir[x][y] & MV_HORIZONTAL)
7078 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7079 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7081 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7082 ResetGfxAnimation(move_x, move_y);
7083 TEST_DrawLevelField(move_x, move_y);
7085 MovDir[x][y] = back_dir;
7087 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7088 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7089 MovDir[x][y] = MV_NONE;
7092 if (MovDir[x][y] & MV_HORIZONTAL &&
7093 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7094 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7095 MovDir[x][y] = MV_NONE;
7100 else if (element == EL_ROBOT ||
7101 element == EL_SATELLITE ||
7102 element == EL_PENGUIN ||
7103 element == EL_EMC_ANDROID)
7105 int attr_x = -1, attr_y = -1;
7116 for (i = 0; i < MAX_PLAYERS; i++)
7118 struct PlayerInfo *player = &stored_player[i];
7119 int jx = player->jx, jy = player->jy;
7121 if (!player->active)
7125 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7133 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7134 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7135 game.engine_version < VERSION_IDENT(3,1,0,0)))
7141 if (element == EL_PENGUIN)
7144 static int xy[4][2] =
7152 for (i = 0; i < NUM_DIRECTIONS; i++)
7154 int ex = x + xy[i][0];
7155 int ey = y + xy[i][1];
7157 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7158 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7159 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7160 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7169 MovDir[x][y] = MV_NONE;
7171 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7172 else if (attr_x > x)
7173 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7175 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7176 else if (attr_y > y)
7177 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7179 if (element == EL_ROBOT)
7183 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7184 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7185 Moving2Blocked(x, y, &newx, &newy);
7187 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7188 MovDelay[x][y] = 8 + 8 * !RND(3);
7190 MovDelay[x][y] = 16;
7192 else if (element == EL_PENGUIN)
7198 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7200 boolean first_horiz = RND(2);
7201 int new_move_dir = MovDir[x][y];
7204 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205 Moving2Blocked(x, y, &newx, &newy);
7207 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7211 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7212 Moving2Blocked(x, y, &newx, &newy);
7214 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7217 MovDir[x][y] = old_move_dir;
7221 else if (element == EL_SATELLITE)
7227 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7229 boolean first_horiz = RND(2);
7230 int new_move_dir = MovDir[x][y];
7233 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7234 Moving2Blocked(x, y, &newx, &newy);
7236 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7240 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7241 Moving2Blocked(x, y, &newx, &newy);
7243 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7246 MovDir[x][y] = old_move_dir;
7250 else if (element == EL_EMC_ANDROID)
7252 static int check_pos[16] =
7254 -1, /* 0 => (invalid) */
7255 7, /* 1 => MV_LEFT */
7256 3, /* 2 => MV_RIGHT */
7257 -1, /* 3 => (invalid) */
7259 0, /* 5 => MV_LEFT | MV_UP */
7260 2, /* 6 => MV_RIGHT | MV_UP */
7261 -1, /* 7 => (invalid) */
7262 5, /* 8 => MV_DOWN */
7263 6, /* 9 => MV_LEFT | MV_DOWN */
7264 4, /* 10 => MV_RIGHT | MV_DOWN */
7265 -1, /* 11 => (invalid) */
7266 -1, /* 12 => (invalid) */
7267 -1, /* 13 => (invalid) */
7268 -1, /* 14 => (invalid) */
7269 -1, /* 15 => (invalid) */
7277 { -1, -1, MV_LEFT | MV_UP },
7279 { +1, -1, MV_RIGHT | MV_UP },
7280 { +1, 0, MV_RIGHT },
7281 { +1, +1, MV_RIGHT | MV_DOWN },
7283 { -1, +1, MV_LEFT | MV_DOWN },
7286 int start_pos, check_order;
7287 boolean can_clone = FALSE;
7290 /* check if there is any free field around current position */
7291 for (i = 0; i < 8; i++)
7293 int newx = x + check_xy[i].dx;
7294 int newy = y + check_xy[i].dy;
7296 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7304 if (can_clone) /* randomly find an element to clone */
7308 start_pos = check_pos[RND(8)];
7309 check_order = (RND(2) ? -1 : +1);
7311 for (i = 0; i < 8; i++)
7313 int pos_raw = start_pos + i * check_order;
7314 int pos = (pos_raw + 8) % 8;
7315 int newx = x + check_xy[pos].dx;
7316 int newy = y + check_xy[pos].dy;
7318 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7320 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7321 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7323 Store[x][y] = Feld[newx][newy];
7332 if (can_clone) /* randomly find a direction to move */
7336 start_pos = check_pos[RND(8)];
7337 check_order = (RND(2) ? -1 : +1);
7339 for (i = 0; i < 8; i++)
7341 int pos_raw = start_pos + i * check_order;
7342 int pos = (pos_raw + 8) % 8;
7343 int newx = x + check_xy[pos].dx;
7344 int newy = y + check_xy[pos].dy;
7345 int new_move_dir = check_xy[pos].dir;
7347 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7349 MovDir[x][y] = new_move_dir;
7350 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7359 if (can_clone) /* cloning and moving successful */
7362 /* cannot clone -- try to move towards player */
7364 start_pos = check_pos[MovDir[x][y] & 0x0f];
7365 check_order = (RND(2) ? -1 : +1);
7367 for (i = 0; i < 3; i++)
7369 /* first check start_pos, then previous/next or (next/previous) pos */
7370 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7371 int pos = (pos_raw + 8) % 8;
7372 int newx = x + check_xy[pos].dx;
7373 int newy = y + check_xy[pos].dy;
7374 int new_move_dir = check_xy[pos].dir;
7376 if (IS_PLAYER(newx, newy))
7379 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7381 MovDir[x][y] = new_move_dir;
7382 MovDelay[x][y] = level.android_move_time * 8 + 1;
7389 else if (move_pattern == MV_TURNING_LEFT ||
7390 move_pattern == MV_TURNING_RIGHT ||
7391 move_pattern == MV_TURNING_LEFT_RIGHT ||
7392 move_pattern == MV_TURNING_RIGHT_LEFT ||
7393 move_pattern == MV_TURNING_RANDOM ||
7394 move_pattern == MV_ALL_DIRECTIONS)
7396 boolean can_turn_left =
7397 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7398 boolean can_turn_right =
7399 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7401 if (element_info[element].move_stepsize == 0) /* "not moving" */
7404 if (move_pattern == MV_TURNING_LEFT)
7405 MovDir[x][y] = left_dir;
7406 else if (move_pattern == MV_TURNING_RIGHT)
7407 MovDir[x][y] = right_dir;
7408 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7409 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7410 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7411 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7412 else if (move_pattern == MV_TURNING_RANDOM)
7413 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7414 can_turn_right && !can_turn_left ? right_dir :
7415 RND(2) ? left_dir : right_dir);
7416 else if (can_turn_left && can_turn_right)
7417 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7418 else if (can_turn_left)
7419 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7420 else if (can_turn_right)
7421 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7423 MovDir[x][y] = back_dir;
7425 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7427 else if (move_pattern == MV_HORIZONTAL ||
7428 move_pattern == MV_VERTICAL)
7430 if (move_pattern & old_move_dir)
7431 MovDir[x][y] = back_dir;
7432 else if (move_pattern == MV_HORIZONTAL)
7433 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7434 else if (move_pattern == MV_VERTICAL)
7435 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7437 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7439 else if (move_pattern & MV_ANY_DIRECTION)
7441 MovDir[x][y] = move_pattern;
7442 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7444 else if (move_pattern & MV_WIND_DIRECTION)
7446 MovDir[x][y] = game.wind_direction;
7447 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7449 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7451 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7452 MovDir[x][y] = left_dir;
7453 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7454 MovDir[x][y] = right_dir;
7456 if (MovDir[x][y] != old_move_dir)
7457 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7459 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7461 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7462 MovDir[x][y] = right_dir;
7463 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7464 MovDir[x][y] = left_dir;
7466 if (MovDir[x][y] != old_move_dir)
7467 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7469 else if (move_pattern == MV_TOWARDS_PLAYER ||
7470 move_pattern == MV_AWAY_FROM_PLAYER)
7472 int attr_x = -1, attr_y = -1;
7474 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7485 for (i = 0; i < MAX_PLAYERS; i++)
7487 struct PlayerInfo *player = &stored_player[i];
7488 int jx = player->jx, jy = player->jy;
7490 if (!player->active)
7494 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7502 MovDir[x][y] = MV_NONE;
7504 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7505 else if (attr_x > x)
7506 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7508 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7509 else if (attr_y > y)
7510 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7512 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7514 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7516 boolean first_horiz = RND(2);
7517 int new_move_dir = MovDir[x][y];
7519 if (element_info[element].move_stepsize == 0) /* "not moving" */
7521 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7522 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7528 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529 Moving2Blocked(x, y, &newx, &newy);
7531 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7535 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7536 Moving2Blocked(x, y, &newx, &newy);
7538 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7541 MovDir[x][y] = old_move_dir;
7544 else if (move_pattern == MV_WHEN_PUSHED ||
7545 move_pattern == MV_WHEN_DROPPED)
7547 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7548 MovDir[x][y] = MV_NONE;
7552 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7554 static int test_xy[7][2] =
7564 static int test_dir[7] =
7574 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7575 int move_preference = -1000000; /* start with very low preference */
7576 int new_move_dir = MV_NONE;
7577 int start_test = RND(4);
7580 for (i = 0; i < NUM_DIRECTIONS; i++)
7582 int move_dir = test_dir[start_test + i];
7583 int move_dir_preference;
7585 xx = x + test_xy[start_test + i][0];
7586 yy = y + test_xy[start_test + i][1];
7588 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7589 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7591 new_move_dir = move_dir;
7596 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7599 move_dir_preference = -1 * RunnerVisit[xx][yy];
7600 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7601 move_dir_preference = PlayerVisit[xx][yy];
7603 if (move_dir_preference > move_preference)
7605 /* prefer field that has not been visited for the longest time */
7606 move_preference = move_dir_preference;
7607 new_move_dir = move_dir;
7609 else if (move_dir_preference == move_preference &&
7610 move_dir == old_move_dir)
7612 /* prefer last direction when all directions are preferred equally */
7613 move_preference = move_dir_preference;
7614 new_move_dir = move_dir;
7618 MovDir[x][y] = new_move_dir;
7619 if (old_move_dir != new_move_dir)
7620 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7624 static void TurnRound(int x, int y)
7626 int direction = MovDir[x][y];
7630 GfxDir[x][y] = MovDir[x][y];
7632 if (direction != MovDir[x][y])
7636 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7638 ResetGfxFrame(x, y, FALSE);
7641 static boolean JustBeingPushed(int x, int y)
7645 for (i = 0; i < MAX_PLAYERS; i++)
7647 struct PlayerInfo *player = &stored_player[i];
7649 if (player->active && player->is_pushing && player->MovPos)
7651 int next_jx = player->jx + (player->jx - player->last_jx);
7652 int next_jy = player->jy + (player->jy - player->last_jy);
7654 if (x == next_jx && y == next_jy)
7662 void StartMoving(int x, int y)
7664 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7665 int element = Feld[x][y];
7670 if (MovDelay[x][y] == 0)
7671 GfxAction[x][y] = ACTION_DEFAULT;
7673 if (CAN_FALL(element) && y < lev_fieldy - 1)
7675 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7676 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7677 if (JustBeingPushed(x, y))
7680 if (element == EL_QUICKSAND_FULL)
7682 if (IS_FREE(x, y + 1))
7684 InitMovingField(x, y, MV_DOWN);
7685 started_moving = TRUE;
7687 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7688 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7689 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7690 Store[x][y] = EL_ROCK;
7692 Store[x][y] = EL_ROCK;
7695 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7697 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7699 if (!MovDelay[x][y])
7701 MovDelay[x][y] = TILEY + 1;
7703 ResetGfxAnimation(x, y);
7704 ResetGfxAnimation(x, y + 1);
7709 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7710 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7717 Feld[x][y] = EL_QUICKSAND_EMPTY;
7718 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7719 Store[x][y + 1] = Store[x][y];
7722 PlayLevelSoundAction(x, y, ACTION_FILLING);
7724 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7726 if (!MovDelay[x][y])
7728 MovDelay[x][y] = TILEY + 1;
7730 ResetGfxAnimation(x, y);
7731 ResetGfxAnimation(x, y + 1);
7736 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7737 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7744 Feld[x][y] = EL_QUICKSAND_EMPTY;
7745 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7746 Store[x][y + 1] = Store[x][y];
7749 PlayLevelSoundAction(x, y, ACTION_FILLING);
7752 else if (element == EL_QUICKSAND_FAST_FULL)
7754 if (IS_FREE(x, y + 1))
7756 InitMovingField(x, y, MV_DOWN);
7757 started_moving = TRUE;
7759 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7760 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7761 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7762 Store[x][y] = EL_ROCK;
7764 Store[x][y] = EL_ROCK;
7767 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7769 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7771 if (!MovDelay[x][y])
7773 MovDelay[x][y] = TILEY + 1;
7775 ResetGfxAnimation(x, y);
7776 ResetGfxAnimation(x, y + 1);
7781 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7782 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7789 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7791 Store[x][y + 1] = Store[x][y];
7794 PlayLevelSoundAction(x, y, ACTION_FILLING);
7796 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7798 if (!MovDelay[x][y])
7800 MovDelay[x][y] = TILEY + 1;
7802 ResetGfxAnimation(x, y);
7803 ResetGfxAnimation(x, y + 1);
7808 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7809 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7816 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7817 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7818 Store[x][y + 1] = Store[x][y];
7821 PlayLevelSoundAction(x, y, ACTION_FILLING);
7824 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7825 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7827 InitMovingField(x, y, MV_DOWN);
7828 started_moving = TRUE;
7830 Feld[x][y] = EL_QUICKSAND_FILLING;
7831 Store[x][y] = element;
7833 PlayLevelSoundAction(x, y, ACTION_FILLING);
7835 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7836 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7838 InitMovingField(x, y, MV_DOWN);
7839 started_moving = TRUE;
7841 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7842 Store[x][y] = element;
7844 PlayLevelSoundAction(x, y, ACTION_FILLING);
7846 else if (element == EL_MAGIC_WALL_FULL)
7848 if (IS_FREE(x, y + 1))
7850 InitMovingField(x, y, MV_DOWN);
7851 started_moving = TRUE;
7853 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7854 Store[x][y] = EL_CHANGED(Store[x][y]);
7856 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7858 if (!MovDelay[x][y])
7859 MovDelay[x][y] = TILEY/4 + 1;
7868 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7869 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7870 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7874 else if (element == EL_BD_MAGIC_WALL_FULL)
7876 if (IS_FREE(x, y + 1))
7878 InitMovingField(x, y, MV_DOWN);
7879 started_moving = TRUE;
7881 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7882 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7884 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7886 if (!MovDelay[x][y])
7887 MovDelay[x][y] = TILEY/4 + 1;
7896 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7897 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7898 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7902 else if (element == EL_DC_MAGIC_WALL_FULL)
7904 if (IS_FREE(x, y + 1))
7906 InitMovingField(x, y, MV_DOWN);
7907 started_moving = TRUE;
7909 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7910 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7912 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7914 if (!MovDelay[x][y])
7915 MovDelay[x][y] = TILEY/4 + 1;
7924 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7925 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7926 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7930 else if ((CAN_PASS_MAGIC_WALL(element) &&
7931 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7932 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7933 (CAN_PASS_DC_MAGIC_WALL(element) &&
7934 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7937 InitMovingField(x, y, MV_DOWN);
7938 started_moving = TRUE;
7941 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7942 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7943 EL_DC_MAGIC_WALL_FILLING);
7944 Store[x][y] = element;
7946 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7948 SplashAcid(x, y + 1);
7950 InitMovingField(x, y, MV_DOWN);
7951 started_moving = TRUE;
7953 Store[x][y] = EL_ACID;
7956 #if USE_FIX_IMPACT_COLLISION
7957 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7958 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7960 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7961 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7963 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7964 CAN_FALL(element) && WasJustFalling[x][y] &&
7965 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7967 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7968 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7969 (Feld[x][y + 1] == EL_BLOCKED)))
7971 /* this is needed for a special case not covered by calling "Impact()"
7972 from "ContinueMoving()": if an element moves to a tile directly below
7973 another element which was just falling on that tile (which was empty
7974 in the previous frame), the falling element above would just stop
7975 instead of smashing the element below (in previous version, the above
7976 element was just checked for "moving" instead of "falling", resulting
7977 in incorrect smashes caused by horizontal movement of the above
7978 element; also, the case of the player being the element to smash was
7979 simply not covered here... :-/ ) */
7981 CheckCollision[x][y] = 0;
7982 CheckImpact[x][y] = 0;
7986 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7988 if (MovDir[x][y] == MV_NONE)
7990 InitMovingField(x, y, MV_DOWN);
7991 started_moving = TRUE;
7994 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7996 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7997 MovDir[x][y] = MV_DOWN;
7999 InitMovingField(x, y, MV_DOWN);
8000 started_moving = TRUE;
8002 else if (element == EL_AMOEBA_DROP)
8004 Feld[x][y] = EL_AMOEBA_GROWING;
8005 Store[x][y] = EL_AMOEBA_WET;
8007 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8008 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8009 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8010 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8012 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8013 (IS_FREE(x - 1, y + 1) ||
8014 Feld[x - 1][y + 1] == EL_ACID));
8015 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8016 (IS_FREE(x + 1, y + 1) ||
8017 Feld[x + 1][y + 1] == EL_ACID));
8018 boolean can_fall_any = (can_fall_left || can_fall_right);
8019 boolean can_fall_both = (can_fall_left && can_fall_right);
8020 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8022 #if USE_NEW_ALL_SLIPPERY
8023 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8025 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8026 can_fall_right = FALSE;
8027 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8028 can_fall_left = FALSE;
8029 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8030 can_fall_right = FALSE;
8031 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8032 can_fall_left = FALSE;
8034 can_fall_any = (can_fall_left || can_fall_right);
8035 can_fall_both = FALSE;
8038 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8040 if (slippery_type == SLIPPERY_ONLY_LEFT)
8041 can_fall_right = FALSE;
8042 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8043 can_fall_left = FALSE;
8044 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8045 can_fall_right = FALSE;
8046 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8047 can_fall_left = FALSE;
8049 can_fall_any = (can_fall_left || can_fall_right);
8050 can_fall_both = (can_fall_left && can_fall_right);
8054 #if USE_NEW_ALL_SLIPPERY
8056 #if USE_NEW_SP_SLIPPERY
8057 /* !!! better use the same properties as for custom elements here !!! */
8058 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8059 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8061 can_fall_right = FALSE; /* slip down on left side */
8062 can_fall_both = FALSE;
8067 #if USE_NEW_ALL_SLIPPERY
8070 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8071 can_fall_right = FALSE; /* slip down on left side */
8073 can_fall_left = !(can_fall_right = RND(2));
8075 can_fall_both = FALSE;
8080 if (game.emulation == EMU_BOULDERDASH ||
8081 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8082 can_fall_right = FALSE; /* slip down on left side */
8084 can_fall_left = !(can_fall_right = RND(2));
8086 can_fall_both = FALSE;
8092 /* if not determined otherwise, prefer left side for slipping down */
8093 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8094 started_moving = TRUE;
8098 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8100 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8103 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8104 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8105 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8106 int belt_dir = game.belt_dir[belt_nr];
8108 if ((belt_dir == MV_LEFT && left_is_free) ||
8109 (belt_dir == MV_RIGHT && right_is_free))
8111 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8113 InitMovingField(x, y, belt_dir);
8114 started_moving = TRUE;
8116 Pushed[x][y] = TRUE;
8117 Pushed[nextx][y] = TRUE;
8119 GfxAction[x][y] = ACTION_DEFAULT;
8123 MovDir[x][y] = 0; /* if element was moving, stop it */
8128 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8130 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8132 if (CAN_MOVE(element) && !started_moving)
8135 int move_pattern = element_info[element].move_pattern;
8140 if (MovDir[x][y] == MV_NONE)
8142 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8143 x, y, element, element_info[element].token_name);
8144 printf("StartMoving(): This should never happen!\n");
8149 Moving2Blocked(x, y, &newx, &newy);
8151 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8154 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8155 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8157 WasJustMoving[x][y] = 0;
8158 CheckCollision[x][y] = 0;
8160 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8162 if (Feld[x][y] != element) /* element has changed */
8166 if (!MovDelay[x][y]) /* start new movement phase */
8168 /* all objects that can change their move direction after each step
8169 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8171 if (element != EL_YAMYAM &&
8172 element != EL_DARK_YAMYAM &&
8173 element != EL_PACMAN &&
8174 !(move_pattern & MV_ANY_DIRECTION) &&
8175 move_pattern != MV_TURNING_LEFT &&
8176 move_pattern != MV_TURNING_RIGHT &&
8177 move_pattern != MV_TURNING_LEFT_RIGHT &&
8178 move_pattern != MV_TURNING_RIGHT_LEFT &&
8179 move_pattern != MV_TURNING_RANDOM)
8183 if (MovDelay[x][y] && (element == EL_BUG ||
8184 element == EL_SPACESHIP ||
8185 element == EL_SP_SNIKSNAK ||
8186 element == EL_SP_ELECTRON ||
8187 element == EL_MOLE))
8188 TEST_DrawLevelField(x, y);
8192 if (MovDelay[x][y]) /* wait some time before next movement */
8196 if (element == EL_ROBOT ||
8197 element == EL_YAMYAM ||
8198 element == EL_DARK_YAMYAM)
8200 DrawLevelElementAnimationIfNeeded(x, y, element);
8201 PlayLevelSoundAction(x, y, ACTION_WAITING);
8203 else if (element == EL_SP_ELECTRON)
8204 DrawLevelElementAnimationIfNeeded(x, y, element);
8205 else if (element == EL_DRAGON)
8208 int dir = MovDir[x][y];
8209 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8210 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8211 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8212 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8213 dir == MV_UP ? IMG_FLAMES_1_UP :
8214 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8215 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8217 GfxAction[x][y] = ACTION_ATTACKING;
8219 if (IS_PLAYER(x, y))
8220 DrawPlayerField(x, y);
8222 TEST_DrawLevelField(x, y);
8224 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8226 for (i = 1; i <= 3; i++)
8228 int xx = x + i * dx;
8229 int yy = y + i * dy;
8230 int sx = SCREENX(xx);
8231 int sy = SCREENY(yy);
8232 int flame_graphic = graphic + (i - 1);
8234 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8239 int flamed = MovingOrBlocked2Element(xx, yy);
8243 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8245 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8246 RemoveMovingField(xx, yy);
8248 RemoveField(xx, yy);
8250 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8253 RemoveMovingField(xx, yy);
8256 ChangeDelay[xx][yy] = 0;
8258 Feld[xx][yy] = EL_FLAMES;
8260 if (IN_SCR_FIELD(sx, sy))
8262 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8263 DrawGraphic(sx, sy, flame_graphic, frame);
8268 if (Feld[xx][yy] == EL_FLAMES)
8269 Feld[xx][yy] = EL_EMPTY;
8270 TEST_DrawLevelField(xx, yy);
8275 if (MovDelay[x][y]) /* element still has to wait some time */
8277 PlayLevelSoundAction(x, y, ACTION_WAITING);
8283 /* now make next step */
8285 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8287 if (DONT_COLLIDE_WITH(element) &&
8288 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8289 !PLAYER_ENEMY_PROTECTED(newx, newy))
8291 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8296 else if (CAN_MOVE_INTO_ACID(element) &&
8297 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8298 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8299 (MovDir[x][y] == MV_DOWN ||
8300 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8302 SplashAcid(newx, newy);
8303 Store[x][y] = EL_ACID;
8305 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8307 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8308 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8309 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8310 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8313 TEST_DrawLevelField(x, y);
8315 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8316 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8317 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8319 local_player->friends_still_needed--;
8320 if (!local_player->friends_still_needed &&
8321 !local_player->GameOver && AllPlayersGone)
8322 PlayerWins(local_player);
8326 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8328 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8329 TEST_DrawLevelField(newx, newy);
8331 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8333 else if (!IS_FREE(newx, newy))
8335 GfxAction[x][y] = ACTION_WAITING;
8337 if (IS_PLAYER(x, y))
8338 DrawPlayerField(x, y);
8340 TEST_DrawLevelField(x, y);
8345 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8347 if (IS_FOOD_PIG(Feld[newx][newy]))
8349 if (IS_MOVING(newx, newy))
8350 RemoveMovingField(newx, newy);
8353 Feld[newx][newy] = EL_EMPTY;
8354 TEST_DrawLevelField(newx, newy);
8357 PlayLevelSound(x, y, SND_PIG_DIGGING);
8359 else if (!IS_FREE(newx, newy))
8361 if (IS_PLAYER(x, y))
8362 DrawPlayerField(x, y);
8364 TEST_DrawLevelField(x, y);
8369 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8371 if (Store[x][y] != EL_EMPTY)
8373 boolean can_clone = FALSE;
8376 /* check if element to clone is still there */
8377 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8379 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8387 /* cannot clone or target field not free anymore -- do not clone */
8388 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8389 Store[x][y] = EL_EMPTY;
8392 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8394 if (IS_MV_DIAGONAL(MovDir[x][y]))
8396 int diagonal_move_dir = MovDir[x][y];
8397 int stored = Store[x][y];
8398 int change_delay = 8;
8401 /* android is moving diagonally */
8403 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8405 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8406 GfxElement[x][y] = EL_EMC_ANDROID;
8407 GfxAction[x][y] = ACTION_SHRINKING;
8408 GfxDir[x][y] = diagonal_move_dir;
8409 ChangeDelay[x][y] = change_delay;
8411 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8414 DrawLevelGraphicAnimation(x, y, graphic);
8415 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8417 if (Feld[newx][newy] == EL_ACID)
8419 SplashAcid(newx, newy);
8424 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8426 Store[newx][newy] = EL_EMC_ANDROID;
8427 GfxElement[newx][newy] = EL_EMC_ANDROID;
8428 GfxAction[newx][newy] = ACTION_GROWING;
8429 GfxDir[newx][newy] = diagonal_move_dir;
8430 ChangeDelay[newx][newy] = change_delay;
8432 graphic = el_act_dir2img(GfxElement[newx][newy],
8433 GfxAction[newx][newy], GfxDir[newx][newy]);
8435 DrawLevelGraphicAnimation(newx, newy, graphic);
8436 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8442 Feld[newx][newy] = EL_EMPTY;
8443 TEST_DrawLevelField(newx, newy);
8445 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8448 else if (!IS_FREE(newx, newy))
8451 if (IS_PLAYER(x, y))
8452 DrawPlayerField(x, y);
8454 TEST_DrawLevelField(x, y);
8460 else if (IS_CUSTOM_ELEMENT(element) &&
8461 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8464 if (!DigFieldByCE(newx, newy, element))
8467 int new_element = Feld[newx][newy];
8469 if (!IS_FREE(newx, newy))
8471 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8472 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8475 /* no element can dig solid indestructible elements */
8476 if (IS_INDESTRUCTIBLE(new_element) &&
8477 !IS_DIGGABLE(new_element) &&
8478 !IS_COLLECTIBLE(new_element))
8481 if (AmoebaNr[newx][newy] &&
8482 (new_element == EL_AMOEBA_FULL ||
8483 new_element == EL_BD_AMOEBA ||
8484 new_element == EL_AMOEBA_GROWING))
8486 AmoebaCnt[AmoebaNr[newx][newy]]--;
8487 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8490 if (IS_MOVING(newx, newy))
8491 RemoveMovingField(newx, newy);
8494 RemoveField(newx, newy);
8495 TEST_DrawLevelField(newx, newy);
8498 /* if digged element was about to explode, prevent the explosion */
8499 ExplodeField[newx][newy] = EX_TYPE_NONE;
8501 PlayLevelSoundAction(x, y, action);
8504 Store[newx][newy] = EL_EMPTY;
8507 /* this makes it possible to leave the removed element again */
8508 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8509 Store[newx][newy] = new_element;
8511 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8513 int move_leave_element = element_info[element].move_leave_element;
8515 /* this makes it possible to leave the removed element again */
8516 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8517 new_element : move_leave_element);
8523 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8525 RunnerVisit[x][y] = FrameCounter;
8526 PlayerVisit[x][y] /= 8; /* expire player visit path */
8529 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8531 if (!IS_FREE(newx, newy))
8533 if (IS_PLAYER(x, y))
8534 DrawPlayerField(x, y);
8536 TEST_DrawLevelField(x, y);
8542 boolean wanna_flame = !RND(10);
8543 int dx = newx - x, dy = newy - y;
8544 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8545 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8546 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8547 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8548 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8549 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8552 IS_CLASSIC_ENEMY(element1) ||
8553 IS_CLASSIC_ENEMY(element2)) &&
8554 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8555 element1 != EL_FLAMES && element2 != EL_FLAMES)
8557 ResetGfxAnimation(x, y);
8558 GfxAction[x][y] = ACTION_ATTACKING;
8560 if (IS_PLAYER(x, y))
8561 DrawPlayerField(x, y);
8563 TEST_DrawLevelField(x, y);
8565 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8567 MovDelay[x][y] = 50;
8571 RemoveField(newx, newy);
8573 Feld[newx][newy] = EL_FLAMES;
8574 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8577 RemoveField(newx1, newy1);
8579 Feld[newx1][newy1] = EL_FLAMES;
8581 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8584 RemoveField(newx2, newy2);
8586 Feld[newx2][newy2] = EL_FLAMES;
8593 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8594 Feld[newx][newy] == EL_DIAMOND)
8596 if (IS_MOVING(newx, newy))
8597 RemoveMovingField(newx, newy);
8600 Feld[newx][newy] = EL_EMPTY;
8601 TEST_DrawLevelField(newx, newy);
8604 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8606 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8607 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8609 if (AmoebaNr[newx][newy])
8611 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8612 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8613 Feld[newx][newy] == EL_BD_AMOEBA)
8614 AmoebaCnt[AmoebaNr[newx][newy]]--;
8619 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8621 RemoveMovingField(newx, newy);
8624 if (IS_MOVING(newx, newy))
8626 RemoveMovingField(newx, newy);
8631 Feld[newx][newy] = EL_EMPTY;
8632 TEST_DrawLevelField(newx, newy);
8635 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8637 else if ((element == EL_PACMAN || element == EL_MOLE)
8638 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8640 if (AmoebaNr[newx][newy])
8642 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8643 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8644 Feld[newx][newy] == EL_BD_AMOEBA)
8645 AmoebaCnt[AmoebaNr[newx][newy]]--;
8648 if (element == EL_MOLE)
8650 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8651 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8653 ResetGfxAnimation(x, y);
8654 GfxAction[x][y] = ACTION_DIGGING;
8655 TEST_DrawLevelField(x, y);
8657 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8659 return; /* wait for shrinking amoeba */
8661 else /* element == EL_PACMAN */
8663 Feld[newx][newy] = EL_EMPTY;
8664 TEST_DrawLevelField(newx, newy);
8665 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8668 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8669 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8670 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8672 /* wait for shrinking amoeba to completely disappear */
8675 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8677 /* object was running against a wall */
8682 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8683 if (move_pattern & MV_ANY_DIRECTION &&
8684 move_pattern == MovDir[x][y])
8686 int blocking_element =
8687 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8689 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8692 element = Feld[x][y]; /* element might have changed */
8696 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8697 DrawLevelElementAnimation(x, y, element);
8699 if (DONT_TOUCH(element))
8700 TestIfBadThingTouchesPlayer(x, y);
8705 InitMovingField(x, y, MovDir[x][y]);
8707 PlayLevelSoundAction(x, y, ACTION_MOVING);
8711 ContinueMoving(x, y);
8714 void ContinueMoving(int x, int y)
8716 int element = Feld[x][y];
8717 struct ElementInfo *ei = &element_info[element];
8718 int direction = MovDir[x][y];
8719 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8720 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8721 int newx = x + dx, newy = y + dy;
8722 int stored = Store[x][y];
8723 int stored_new = Store[newx][newy];
8724 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8725 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8726 boolean last_line = (newy == lev_fieldy - 1);
8728 MovPos[x][y] += getElementMoveStepsize(x, y);
8730 if (pushed_by_player) /* special case: moving object pushed by player */
8731 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8733 if (ABS(MovPos[x][y]) < TILEX)
8736 int ee = Feld[x][y];
8737 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8738 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8740 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8741 x, y, ABS(MovPos[x][y]),
8743 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8746 TEST_DrawLevelField(x, y);
8748 return; /* element is still moving */
8751 /* element reached destination field */
8753 Feld[x][y] = EL_EMPTY;
8754 Feld[newx][newy] = element;
8755 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8757 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8759 element = Feld[newx][newy] = EL_ACID;
8761 else if (element == EL_MOLE)
8763 Feld[x][y] = EL_SAND;
8765 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8767 else if (element == EL_QUICKSAND_FILLING)
8769 element = Feld[newx][newy] = get_next_element(element);
8770 Store[newx][newy] = Store[x][y];
8772 else if (element == EL_QUICKSAND_EMPTYING)
8774 Feld[x][y] = get_next_element(element);
8775 element = Feld[newx][newy] = Store[x][y];
8777 else if (element == EL_QUICKSAND_FAST_FILLING)
8779 element = Feld[newx][newy] = get_next_element(element);
8780 Store[newx][newy] = Store[x][y];
8782 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8784 Feld[x][y] = get_next_element(element);
8785 element = Feld[newx][newy] = Store[x][y];
8787 else if (element == EL_MAGIC_WALL_FILLING)
8789 element = Feld[newx][newy] = get_next_element(element);
8790 if (!game.magic_wall_active)
8791 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8792 Store[newx][newy] = Store[x][y];
8794 else if (element == EL_MAGIC_WALL_EMPTYING)
8796 Feld[x][y] = get_next_element(element);
8797 if (!game.magic_wall_active)
8798 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8799 element = Feld[newx][newy] = Store[x][y];
8801 #if USE_NEW_CUSTOM_VALUE
8802 InitField(newx, newy, FALSE);
8805 else if (element == EL_BD_MAGIC_WALL_FILLING)
8807 element = Feld[newx][newy] = get_next_element(element);
8808 if (!game.magic_wall_active)
8809 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8810 Store[newx][newy] = Store[x][y];
8812 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8814 Feld[x][y] = get_next_element(element);
8815 if (!game.magic_wall_active)
8816 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8817 element = Feld[newx][newy] = Store[x][y];
8819 #if USE_NEW_CUSTOM_VALUE
8820 InitField(newx, newy, FALSE);
8823 else if (element == EL_DC_MAGIC_WALL_FILLING)
8825 element = Feld[newx][newy] = get_next_element(element);
8826 if (!game.magic_wall_active)
8827 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8828 Store[newx][newy] = Store[x][y];
8830 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8832 Feld[x][y] = get_next_element(element);
8833 if (!game.magic_wall_active)
8834 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8835 element = Feld[newx][newy] = Store[x][y];
8837 #if USE_NEW_CUSTOM_VALUE
8838 InitField(newx, newy, FALSE);
8841 else if (element == EL_AMOEBA_DROPPING)
8843 Feld[x][y] = get_next_element(element);
8844 element = Feld[newx][newy] = Store[x][y];
8846 else if (element == EL_SOKOBAN_OBJECT)
8849 Feld[x][y] = Back[x][y];
8851 if (Back[newx][newy])
8852 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8854 Back[x][y] = Back[newx][newy] = 0;
8857 Store[x][y] = EL_EMPTY;
8862 MovDelay[newx][newy] = 0;
8864 if (CAN_CHANGE_OR_HAS_ACTION(element))
8866 /* copy element change control values to new field */
8867 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8868 ChangePage[newx][newy] = ChangePage[x][y];
8869 ChangeCount[newx][newy] = ChangeCount[x][y];
8870 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8873 #if USE_NEW_CUSTOM_VALUE
8874 CustomValue[newx][newy] = CustomValue[x][y];
8877 ChangeDelay[x][y] = 0;
8878 ChangePage[x][y] = -1;
8879 ChangeCount[x][y] = 0;
8880 ChangeEvent[x][y] = -1;
8882 #if USE_NEW_CUSTOM_VALUE
8883 CustomValue[x][y] = 0;
8886 /* copy animation control values to new field */
8887 GfxFrame[newx][newy] = GfxFrame[x][y];
8888 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8889 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8890 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8892 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8894 /* some elements can leave other elements behind after moving */
8896 if (ei->move_leave_element != EL_EMPTY &&
8897 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8898 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8900 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8901 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8902 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8905 int move_leave_element = ei->move_leave_element;
8909 /* this makes it possible to leave the removed element again */
8910 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8911 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8913 /* this makes it possible to leave the removed element again */
8914 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8915 move_leave_element = stored;
8918 /* this makes it possible to leave the removed element again */
8919 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8920 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8921 move_leave_element = stored;
8924 Feld[x][y] = move_leave_element;
8926 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8927 MovDir[x][y] = direction;
8929 InitField(x, y, FALSE);
8931 if (GFX_CRUMBLED(Feld[x][y]))
8932 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8934 if (ELEM_IS_PLAYER(move_leave_element))
8935 RelocatePlayer(x, y, move_leave_element);
8938 /* do this after checking for left-behind element */
8939 ResetGfxAnimation(x, y); /* reset animation values for old field */
8941 if (!CAN_MOVE(element) ||
8942 (CAN_FALL(element) && direction == MV_DOWN &&
8943 (element == EL_SPRING ||
8944 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8945 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8946 GfxDir[x][y] = MovDir[newx][newy] = 0;
8948 TEST_DrawLevelField(x, y);
8949 TEST_DrawLevelField(newx, newy);
8951 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8953 /* prevent pushed element from moving on in pushed direction */
8954 if (pushed_by_player && CAN_MOVE(element) &&
8955 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8956 !(element_info[element].move_pattern & direction))
8957 TurnRound(newx, newy);
8959 /* prevent elements on conveyor belt from moving on in last direction */
8960 if (pushed_by_conveyor && CAN_FALL(element) &&
8961 direction & MV_HORIZONTAL)
8962 MovDir[newx][newy] = 0;
8964 if (!pushed_by_player)
8966 int nextx = newx + dx, nexty = newy + dy;
8967 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8969 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8971 if (CAN_FALL(element) && direction == MV_DOWN)
8972 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8974 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8975 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8977 #if USE_FIX_IMPACT_COLLISION
8978 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8979 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8983 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8985 TestIfBadThingTouchesPlayer(newx, newy);
8986 TestIfBadThingTouchesFriend(newx, newy);
8988 if (!IS_CUSTOM_ELEMENT(element))
8989 TestIfBadThingTouchesOtherBadThing(newx, newy);
8991 else if (element == EL_PENGUIN)
8992 TestIfFriendTouchesBadThing(newx, newy);
8994 if (DONT_GET_HIT_BY(element))
8996 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8999 /* give the player one last chance (one more frame) to move away */
9000 if (CAN_FALL(element) && direction == MV_DOWN &&
9001 (last_line || (!IS_FREE(x, newy + 1) &&
9002 (!IS_PLAYER(x, newy + 1) ||
9003 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9006 if (pushed_by_player && !game.use_change_when_pushing_bug)
9008 int push_side = MV_DIR_OPPOSITE(direction);
9009 struct PlayerInfo *player = PLAYERINFO(x, y);
9011 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9012 player->index_bit, push_side);
9013 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9014 player->index_bit, push_side);
9017 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9018 MovDelay[newx][newy] = 1;
9020 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9022 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9025 if (ChangePage[newx][newy] != -1) /* delayed change */
9027 int page = ChangePage[newx][newy];
9028 struct ElementChangeInfo *change = &ei->change_page[page];
9030 ChangePage[newx][newy] = -1;
9032 if (change->can_change)
9034 if (ChangeElement(newx, newy, element, page))
9036 if (change->post_change_function)
9037 change->post_change_function(newx, newy);
9041 if (change->has_action)
9042 ExecuteCustomElementAction(newx, newy, element, page);
9046 TestIfElementHitsCustomElement(newx, newy, direction);
9047 TestIfPlayerTouchesCustomElement(newx, newy);
9048 TestIfElementTouchesCustomElement(newx, newy);
9050 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9051 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9052 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9053 MV_DIR_OPPOSITE(direction));
9056 int AmoebeNachbarNr(int ax, int ay)
9059 int element = Feld[ax][ay];
9061 static int xy[4][2] =
9069 for (i = 0; i < NUM_DIRECTIONS; i++)
9071 int x = ax + xy[i][0];
9072 int y = ay + xy[i][1];
9074 if (!IN_LEV_FIELD(x, y))
9077 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9078 group_nr = AmoebaNr[x][y];
9084 void AmoebenVereinigen(int ax, int ay)
9086 int i, x, y, xx, yy;
9087 int new_group_nr = AmoebaNr[ax][ay];
9088 static int xy[4][2] =
9096 if (new_group_nr == 0)
9099 for (i = 0; i < NUM_DIRECTIONS; i++)
9104 if (!IN_LEV_FIELD(x, y))
9107 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9108 Feld[x][y] == EL_BD_AMOEBA ||
9109 Feld[x][y] == EL_AMOEBA_DEAD) &&
9110 AmoebaNr[x][y] != new_group_nr)
9112 int old_group_nr = AmoebaNr[x][y];
9114 if (old_group_nr == 0)
9117 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9118 AmoebaCnt[old_group_nr] = 0;
9119 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9120 AmoebaCnt2[old_group_nr] = 0;
9122 SCAN_PLAYFIELD(xx, yy)
9124 if (AmoebaNr[xx][yy] == old_group_nr)
9125 AmoebaNr[xx][yy] = new_group_nr;
9131 void AmoebeUmwandeln(int ax, int ay)
9135 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9137 int group_nr = AmoebaNr[ax][ay];
9142 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9143 printf("AmoebeUmwandeln(): This should never happen!\n");
9148 SCAN_PLAYFIELD(x, y)
9150 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9153 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9157 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9158 SND_AMOEBA_TURNING_TO_GEM :
9159 SND_AMOEBA_TURNING_TO_ROCK));
9164 static int xy[4][2] =
9172 for (i = 0; i < NUM_DIRECTIONS; i++)
9177 if (!IN_LEV_FIELD(x, y))
9180 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9182 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9183 SND_AMOEBA_TURNING_TO_GEM :
9184 SND_AMOEBA_TURNING_TO_ROCK));
9191 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9194 int group_nr = AmoebaNr[ax][ay];
9195 boolean done = FALSE;
9200 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9201 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9206 SCAN_PLAYFIELD(x, y)
9208 if (AmoebaNr[x][y] == group_nr &&
9209 (Feld[x][y] == EL_AMOEBA_DEAD ||
9210 Feld[x][y] == EL_BD_AMOEBA ||
9211 Feld[x][y] == EL_AMOEBA_GROWING))
9214 Feld[x][y] = new_element;
9215 InitField(x, y, FALSE);
9216 TEST_DrawLevelField(x, y);
9222 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9223 SND_BD_AMOEBA_TURNING_TO_ROCK :
9224 SND_BD_AMOEBA_TURNING_TO_GEM));
9227 void AmoebeWaechst(int x, int y)
9229 static unsigned long sound_delay = 0;
9230 static unsigned long sound_delay_value = 0;
9232 if (!MovDelay[x][y]) /* start new growing cycle */
9236 if (DelayReached(&sound_delay, sound_delay_value))
9238 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9239 sound_delay_value = 30;
9243 if (MovDelay[x][y]) /* wait some time before growing bigger */
9246 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9248 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9249 6 - MovDelay[x][y]);
9251 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9254 if (!MovDelay[x][y])
9256 Feld[x][y] = Store[x][y];
9258 TEST_DrawLevelField(x, y);
9263 void AmoebaDisappearing(int x, int y)
9265 static unsigned long sound_delay = 0;
9266 static unsigned long sound_delay_value = 0;
9268 if (!MovDelay[x][y]) /* start new shrinking cycle */
9272 if (DelayReached(&sound_delay, sound_delay_value))
9273 sound_delay_value = 30;
9276 if (MovDelay[x][y]) /* wait some time before shrinking */
9279 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9281 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9282 6 - MovDelay[x][y]);
9284 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9287 if (!MovDelay[x][y])
9289 Feld[x][y] = EL_EMPTY;
9290 TEST_DrawLevelField(x, y);
9292 /* don't let mole enter this field in this cycle;
9293 (give priority to objects falling to this field from above) */
9299 void AmoebeAbleger(int ax, int ay)
9302 int element = Feld[ax][ay];
9303 int graphic = el2img(element);
9304 int newax = ax, neway = ay;
9305 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9306 static int xy[4][2] =
9314 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9316 Feld[ax][ay] = EL_AMOEBA_DEAD;
9317 TEST_DrawLevelField(ax, ay);
9321 if (IS_ANIMATED(graphic))
9322 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9324 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9325 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9327 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9330 if (MovDelay[ax][ay])
9334 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9337 int x = ax + xy[start][0];
9338 int y = ay + xy[start][1];
9340 if (!IN_LEV_FIELD(x, y))
9343 if (IS_FREE(x, y) ||
9344 CAN_GROW_INTO(Feld[x][y]) ||
9345 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9346 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9352 if (newax == ax && neway == ay)
9355 else /* normal or "filled" (BD style) amoeba */
9358 boolean waiting_for_player = FALSE;
9360 for (i = 0; i < NUM_DIRECTIONS; i++)
9362 int j = (start + i) % 4;
9363 int x = ax + xy[j][0];
9364 int y = ay + xy[j][1];
9366 if (!IN_LEV_FIELD(x, y))
9369 if (IS_FREE(x, y) ||
9370 CAN_GROW_INTO(Feld[x][y]) ||
9371 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9372 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9378 else if (IS_PLAYER(x, y))
9379 waiting_for_player = TRUE;
9382 if (newax == ax && neway == ay) /* amoeba cannot grow */
9384 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9386 Feld[ax][ay] = EL_AMOEBA_DEAD;
9387 TEST_DrawLevelField(ax, ay);
9388 AmoebaCnt[AmoebaNr[ax][ay]]--;
9390 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9392 if (element == EL_AMOEBA_FULL)
9393 AmoebeUmwandeln(ax, ay);
9394 else if (element == EL_BD_AMOEBA)
9395 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9400 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9402 /* amoeba gets larger by growing in some direction */
9404 int new_group_nr = AmoebaNr[ax][ay];
9407 if (new_group_nr == 0)
9409 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9410 printf("AmoebeAbleger(): This should never happen!\n");
9415 AmoebaNr[newax][neway] = new_group_nr;
9416 AmoebaCnt[new_group_nr]++;
9417 AmoebaCnt2[new_group_nr]++;
9419 /* if amoeba touches other amoeba(s) after growing, unify them */
9420 AmoebenVereinigen(newax, neway);
9422 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9424 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9430 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9431 (neway == lev_fieldy - 1 && newax != ax))
9433 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9434 Store[newax][neway] = element;
9436 else if (neway == ay || element == EL_EMC_DRIPPER)
9438 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9440 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9444 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9445 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9446 Store[ax][ay] = EL_AMOEBA_DROP;
9447 ContinueMoving(ax, ay);
9451 TEST_DrawLevelField(newax, neway);
9454 void Life(int ax, int ay)
9458 int element = Feld[ax][ay];
9459 int graphic = el2img(element);
9460 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9462 boolean changed = FALSE;
9464 if (IS_ANIMATED(graphic))
9465 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9470 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9471 MovDelay[ax][ay] = life_time;
9473 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9476 if (MovDelay[ax][ay])
9480 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9482 int xx = ax+x1, yy = ay+y1;
9485 if (!IN_LEV_FIELD(xx, yy))
9488 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9490 int x = xx+x2, y = yy+y2;
9492 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9495 if (((Feld[x][y] == element ||
9496 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9498 (IS_FREE(x, y) && Stop[x][y]))
9502 if (xx == ax && yy == ay) /* field in the middle */
9504 if (nachbarn < life_parameter[0] ||
9505 nachbarn > life_parameter[1])
9507 Feld[xx][yy] = EL_EMPTY;
9509 TEST_DrawLevelField(xx, yy);
9510 Stop[xx][yy] = TRUE;
9514 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9515 { /* free border field */
9516 if (nachbarn >= life_parameter[2] &&
9517 nachbarn <= life_parameter[3])
9519 Feld[xx][yy] = element;
9520 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9522 TEST_DrawLevelField(xx, yy);
9523 Stop[xx][yy] = TRUE;
9530 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9531 SND_GAME_OF_LIFE_GROWING);
9534 static void InitRobotWheel(int x, int y)
9536 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9539 static void RunRobotWheel(int x, int y)
9541 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9544 static void StopRobotWheel(int x, int y)
9546 if (ZX == x && ZY == y)
9550 game.robot_wheel_active = FALSE;
9554 static void InitTimegateWheel(int x, int y)
9556 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9559 static void RunTimegateWheel(int x, int y)
9561 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9564 static void InitMagicBallDelay(int x, int y)
9567 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9569 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9573 static void ActivateMagicBall(int bx, int by)
9577 if (level.ball_random)
9579 int pos_border = RND(8); /* select one of the eight border elements */
9580 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9581 int xx = pos_content % 3;
9582 int yy = pos_content / 3;
9587 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9588 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9592 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9594 int xx = x - bx + 1;
9595 int yy = y - by + 1;
9597 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9598 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9602 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9605 void CheckExit(int x, int y)
9607 if (local_player->gems_still_needed > 0 ||
9608 local_player->sokobanfields_still_needed > 0 ||
9609 local_player->lights_still_needed > 0)
9611 int element = Feld[x][y];
9612 int graphic = el2img(element);
9614 if (IS_ANIMATED(graphic))
9615 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9620 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9623 Feld[x][y] = EL_EXIT_OPENING;
9625 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9628 void CheckExitEM(int x, int y)
9630 if (local_player->gems_still_needed > 0 ||
9631 local_player->sokobanfields_still_needed > 0 ||
9632 local_player->lights_still_needed > 0)
9634 int element = Feld[x][y];
9635 int graphic = el2img(element);
9637 if (IS_ANIMATED(graphic))
9638 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9643 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9646 Feld[x][y] = EL_EM_EXIT_OPENING;
9648 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9651 void CheckExitSteel(int x, int y)
9653 if (local_player->gems_still_needed > 0 ||
9654 local_player->sokobanfields_still_needed > 0 ||
9655 local_player->lights_still_needed > 0)
9657 int element = Feld[x][y];
9658 int graphic = el2img(element);
9660 if (IS_ANIMATED(graphic))
9661 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9666 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9669 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9671 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9674 void CheckExitSteelEM(int x, int y)
9676 if (local_player->gems_still_needed > 0 ||
9677 local_player->sokobanfields_still_needed > 0 ||
9678 local_player->lights_still_needed > 0)
9680 int element = Feld[x][y];
9681 int graphic = el2img(element);
9683 if (IS_ANIMATED(graphic))
9684 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9689 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9692 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9694 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9697 void CheckExitSP(int x, int y)
9699 if (local_player->gems_still_needed > 0)
9701 int element = Feld[x][y];
9702 int graphic = el2img(element);
9704 if (IS_ANIMATED(graphic))
9705 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9710 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9713 Feld[x][y] = EL_SP_EXIT_OPENING;
9715 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9718 static void CloseAllOpenTimegates()
9722 SCAN_PLAYFIELD(x, y)
9724 int element = Feld[x][y];
9726 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9728 Feld[x][y] = EL_TIMEGATE_CLOSING;
9730 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9735 void DrawTwinkleOnField(int x, int y)
9737 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9740 if (Feld[x][y] == EL_BD_DIAMOND)
9743 if (MovDelay[x][y] == 0) /* next animation frame */
9744 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9746 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9750 DrawLevelElementAnimation(x, y, Feld[x][y]);
9752 if (MovDelay[x][y] != 0)
9754 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9755 10 - MovDelay[x][y]);
9757 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9762 void MauerWaechst(int x, int y)
9766 if (!MovDelay[x][y]) /* next animation frame */
9767 MovDelay[x][y] = 3 * delay;
9769 if (MovDelay[x][y]) /* wait some time before next frame */
9773 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9775 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9776 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9778 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9781 if (!MovDelay[x][y])
9783 if (MovDir[x][y] == MV_LEFT)
9785 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9786 TEST_DrawLevelField(x - 1, y);
9788 else if (MovDir[x][y] == MV_RIGHT)
9790 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9791 TEST_DrawLevelField(x + 1, y);
9793 else if (MovDir[x][y] == MV_UP)
9795 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9796 TEST_DrawLevelField(x, y - 1);
9800 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9801 TEST_DrawLevelField(x, y + 1);
9804 Feld[x][y] = Store[x][y];
9806 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9807 TEST_DrawLevelField(x, y);
9812 void MauerAbleger(int ax, int ay)
9814 int element = Feld[ax][ay];
9815 int graphic = el2img(element);
9816 boolean oben_frei = FALSE, unten_frei = FALSE;
9817 boolean links_frei = FALSE, rechts_frei = FALSE;
9818 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9819 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9820 boolean new_wall = FALSE;
9822 if (IS_ANIMATED(graphic))
9823 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9825 if (!MovDelay[ax][ay]) /* start building new wall */
9826 MovDelay[ax][ay] = 6;
9828 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9831 if (MovDelay[ax][ay])
9835 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9837 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9839 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9841 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9844 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9845 element == EL_EXPANDABLE_WALL_ANY)
9849 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9850 Store[ax][ay-1] = element;
9851 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9852 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9853 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9854 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9859 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9860 Store[ax][ay+1] = element;
9861 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9862 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9863 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9864 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9869 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9870 element == EL_EXPANDABLE_WALL_ANY ||
9871 element == EL_EXPANDABLE_WALL ||
9872 element == EL_BD_EXPANDABLE_WALL)
9876 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9877 Store[ax-1][ay] = element;
9878 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9879 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9880 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9881 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9887 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9888 Store[ax+1][ay] = element;
9889 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9890 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9891 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9892 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9897 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9898 TEST_DrawLevelField(ax, ay);
9900 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9902 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9903 unten_massiv = TRUE;
9904 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9905 links_massiv = TRUE;
9906 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9907 rechts_massiv = TRUE;
9909 if (((oben_massiv && unten_massiv) ||
9910 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9911 element == EL_EXPANDABLE_WALL) &&
9912 ((links_massiv && rechts_massiv) ||
9913 element == EL_EXPANDABLE_WALL_VERTICAL))
9914 Feld[ax][ay] = EL_WALL;
9917 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9920 void MauerAblegerStahl(int ax, int ay)
9922 int element = Feld[ax][ay];
9923 int graphic = el2img(element);
9924 boolean oben_frei = FALSE, unten_frei = FALSE;
9925 boolean links_frei = FALSE, rechts_frei = FALSE;
9926 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9927 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9928 boolean new_wall = FALSE;
9930 if (IS_ANIMATED(graphic))
9931 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9933 if (!MovDelay[ax][ay]) /* start building new wall */
9934 MovDelay[ax][ay] = 6;
9936 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9939 if (MovDelay[ax][ay])
9943 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9945 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9947 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9949 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9952 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9953 element == EL_EXPANDABLE_STEELWALL_ANY)
9957 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9958 Store[ax][ay-1] = element;
9959 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9960 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9961 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9962 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9967 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9968 Store[ax][ay+1] = element;
9969 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9970 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9971 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9972 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9977 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9978 element == EL_EXPANDABLE_STEELWALL_ANY)
9982 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9983 Store[ax-1][ay] = element;
9984 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9985 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9986 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9987 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9993 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9994 Store[ax+1][ay] = element;
9995 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9996 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9997 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9998 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10003 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10004 oben_massiv = TRUE;
10005 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10006 unten_massiv = TRUE;
10007 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10008 links_massiv = TRUE;
10009 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10010 rechts_massiv = TRUE;
10012 if (((oben_massiv && unten_massiv) ||
10013 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10014 ((links_massiv && rechts_massiv) ||
10015 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10016 Feld[ax][ay] = EL_STEELWALL;
10019 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10022 void CheckForDragon(int x, int y)
10025 boolean dragon_found = FALSE;
10026 static int xy[4][2] =
10034 for (i = 0; i < NUM_DIRECTIONS; i++)
10036 for (j = 0; j < 4; j++)
10038 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10040 if (IN_LEV_FIELD(xx, yy) &&
10041 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10043 if (Feld[xx][yy] == EL_DRAGON)
10044 dragon_found = TRUE;
10053 for (i = 0; i < NUM_DIRECTIONS; i++)
10055 for (j = 0; j < 3; j++)
10057 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10059 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10061 Feld[xx][yy] = EL_EMPTY;
10062 TEST_DrawLevelField(xx, yy);
10071 static void InitBuggyBase(int x, int y)
10073 int element = Feld[x][y];
10074 int activating_delay = FRAMES_PER_SECOND / 4;
10076 ChangeDelay[x][y] =
10077 (element == EL_SP_BUGGY_BASE ?
10078 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10079 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10081 element == EL_SP_BUGGY_BASE_ACTIVE ?
10082 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10085 static void WarnBuggyBase(int x, int y)
10088 static int xy[4][2] =
10096 for (i = 0; i < NUM_DIRECTIONS; i++)
10098 int xx = x + xy[i][0];
10099 int yy = y + xy[i][1];
10101 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10103 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10110 static void InitTrap(int x, int y)
10112 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10115 static void ActivateTrap(int x, int y)
10117 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10120 static void ChangeActiveTrap(int x, int y)
10122 int graphic = IMG_TRAP_ACTIVE;
10124 /* if new animation frame was drawn, correct crumbled sand border */
10125 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10126 TEST_DrawLevelFieldCrumbledSand(x, y);
10129 static int getSpecialActionElement(int element, int number, int base_element)
10131 return (element != EL_EMPTY ? element :
10132 number != -1 ? base_element + number - 1 :
10136 static int getModifiedActionNumber(int value_old, int operator, int operand,
10137 int value_min, int value_max)
10139 int value_new = (operator == CA_MODE_SET ? operand :
10140 operator == CA_MODE_ADD ? value_old + operand :
10141 operator == CA_MODE_SUBTRACT ? value_old - operand :
10142 operator == CA_MODE_MULTIPLY ? value_old * operand :
10143 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10144 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10147 return (value_new < value_min ? value_min :
10148 value_new > value_max ? value_max :
10152 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10154 struct ElementInfo *ei = &element_info[element];
10155 struct ElementChangeInfo *change = &ei->change_page[page];
10156 int target_element = change->target_element;
10157 int action_type = change->action_type;
10158 int action_mode = change->action_mode;
10159 int action_arg = change->action_arg;
10160 int action_element = change->action_element;
10163 if (!change->has_action)
10166 /* ---------- determine action paramater values -------------------------- */
10168 int level_time_value =
10169 (level.time > 0 ? TimeLeft :
10172 int action_arg_element_raw =
10173 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10174 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10175 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10176 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10177 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10178 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10179 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10181 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10184 if (action_arg_element_raw == EL_GROUP_START)
10185 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10188 int action_arg_direction =
10189 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10190 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10191 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10192 change->actual_trigger_side :
10193 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10194 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10197 int action_arg_number_min =
10198 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10201 int action_arg_number_max =
10202 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10203 action_type == CA_SET_LEVEL_GEMS ? 999 :
10204 action_type == CA_SET_LEVEL_TIME ? 9999 :
10205 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10206 action_type == CA_SET_CE_VALUE ? 9999 :
10207 action_type == CA_SET_CE_SCORE ? 9999 :
10210 int action_arg_number_reset =
10211 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10212 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10213 action_type == CA_SET_LEVEL_TIME ? level.time :
10214 action_type == CA_SET_LEVEL_SCORE ? 0 :
10215 #if USE_NEW_CUSTOM_VALUE
10216 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10218 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10220 action_type == CA_SET_CE_SCORE ? 0 :
10223 int action_arg_number =
10224 (action_arg <= CA_ARG_MAX ? action_arg :
10225 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10226 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10227 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10228 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10229 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10230 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10231 #if USE_NEW_CUSTOM_VALUE
10232 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10234 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10236 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10237 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10238 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10239 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10240 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10241 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10242 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10243 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10244 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10245 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10246 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10247 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10248 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10249 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10252 int action_arg_number_old =
10253 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10254 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10255 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10256 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10257 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10260 int action_arg_number_new =
10261 getModifiedActionNumber(action_arg_number_old,
10262 action_mode, action_arg_number,
10263 action_arg_number_min, action_arg_number_max);
10266 int trigger_player_bits =
10267 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10268 change->actual_trigger_player_bits : change->trigger_player);
10270 int trigger_player_bits =
10271 (change->actual_trigger_player >= EL_PLAYER_1 &&
10272 change->actual_trigger_player <= EL_PLAYER_4 ?
10273 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10277 int action_arg_player_bits =
10278 (action_arg >= CA_ARG_PLAYER_1 &&
10279 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10280 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10281 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10284 /* ---------- execute action -------------------------------------------- */
10286 switch (action_type)
10293 /* ---------- level actions ------------------------------------------- */
10295 case CA_RESTART_LEVEL:
10297 game.restart_level = TRUE;
10302 case CA_SHOW_ENVELOPE:
10304 int element = getSpecialActionElement(action_arg_element,
10305 action_arg_number, EL_ENVELOPE_1);
10307 if (IS_ENVELOPE(element))
10308 local_player->show_envelope = element;
10313 case CA_SET_LEVEL_TIME:
10315 if (level.time > 0) /* only modify limited time value */
10317 TimeLeft = action_arg_number_new;
10320 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10322 DisplayGameControlValues();
10324 DrawGameValue_Time(TimeLeft);
10327 if (!TimeLeft && setup.time_limit)
10328 for (i = 0; i < MAX_PLAYERS; i++)
10329 KillPlayer(&stored_player[i]);
10335 case CA_SET_LEVEL_SCORE:
10337 local_player->score = action_arg_number_new;
10340 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10342 DisplayGameControlValues();
10344 DrawGameValue_Score(local_player->score);
10350 case CA_SET_LEVEL_GEMS:
10352 local_player->gems_still_needed = action_arg_number_new;
10355 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10357 DisplayGameControlValues();
10359 DrawGameValue_Emeralds(local_player->gems_still_needed);
10365 #if !USE_PLAYER_GRAVITY
10366 case CA_SET_LEVEL_GRAVITY:
10368 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10369 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10370 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10376 case CA_SET_LEVEL_WIND:
10378 game.wind_direction = action_arg_direction;
10383 /* ---------- player actions ------------------------------------------ */
10385 case CA_MOVE_PLAYER:
10387 /* automatically move to the next field in specified direction */
10388 for (i = 0; i < MAX_PLAYERS; i++)
10389 if (trigger_player_bits & (1 << i))
10390 stored_player[i].programmed_action = action_arg_direction;
10395 case CA_EXIT_PLAYER:
10397 for (i = 0; i < MAX_PLAYERS; i++)
10398 if (action_arg_player_bits & (1 << i))
10399 PlayerWins(&stored_player[i]);
10404 case CA_KILL_PLAYER:
10406 for (i = 0; i < MAX_PLAYERS; i++)
10407 if (action_arg_player_bits & (1 << i))
10408 KillPlayer(&stored_player[i]);
10413 case CA_SET_PLAYER_KEYS:
10415 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10416 int element = getSpecialActionElement(action_arg_element,
10417 action_arg_number, EL_KEY_1);
10419 if (IS_KEY(element))
10421 for (i = 0; i < MAX_PLAYERS; i++)
10423 if (trigger_player_bits & (1 << i))
10425 stored_player[i].key[KEY_NR(element)] = key_state;
10427 DrawGameDoorValues();
10435 case CA_SET_PLAYER_SPEED:
10438 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10441 for (i = 0; i < MAX_PLAYERS; i++)
10443 if (trigger_player_bits & (1 << i))
10445 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10447 if (action_arg == CA_ARG_SPEED_FASTER &&
10448 stored_player[i].cannot_move)
10450 action_arg_number = STEPSIZE_VERY_SLOW;
10452 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10453 action_arg == CA_ARG_SPEED_FASTER)
10455 action_arg_number = 2;
10456 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10459 else if (action_arg == CA_ARG_NUMBER_RESET)
10461 action_arg_number = level.initial_player_stepsize[i];
10465 getModifiedActionNumber(move_stepsize,
10468 action_arg_number_min,
10469 action_arg_number_max);
10471 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10478 case CA_SET_PLAYER_SHIELD:
10480 for (i = 0; i < MAX_PLAYERS; i++)
10482 if (trigger_player_bits & (1 << i))
10484 if (action_arg == CA_ARG_SHIELD_OFF)
10486 stored_player[i].shield_normal_time_left = 0;
10487 stored_player[i].shield_deadly_time_left = 0;
10489 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10491 stored_player[i].shield_normal_time_left = 999999;
10493 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10495 stored_player[i].shield_normal_time_left = 999999;
10496 stored_player[i].shield_deadly_time_left = 999999;
10504 #if USE_PLAYER_GRAVITY
10505 case CA_SET_PLAYER_GRAVITY:
10507 for (i = 0; i < MAX_PLAYERS; i++)
10509 if (trigger_player_bits & (1 << i))
10511 stored_player[i].gravity =
10512 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10513 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10514 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10515 stored_player[i].gravity);
10523 case CA_SET_PLAYER_ARTWORK:
10525 for (i = 0; i < MAX_PLAYERS; i++)
10527 if (trigger_player_bits & (1 << i))
10529 int artwork_element = action_arg_element;
10531 if (action_arg == CA_ARG_ELEMENT_RESET)
10533 (level.use_artwork_element[i] ? level.artwork_element[i] :
10534 stored_player[i].element_nr);
10536 #if USE_GFX_RESET_PLAYER_ARTWORK
10537 if (stored_player[i].artwork_element != artwork_element)
10538 stored_player[i].Frame = 0;
10541 stored_player[i].artwork_element = artwork_element;
10543 SetPlayerWaiting(&stored_player[i], FALSE);
10545 /* set number of special actions for bored and sleeping animation */
10546 stored_player[i].num_special_action_bored =
10547 get_num_special_action(artwork_element,
10548 ACTION_BORING_1, ACTION_BORING_LAST);
10549 stored_player[i].num_special_action_sleeping =
10550 get_num_special_action(artwork_element,
10551 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10558 case CA_SET_PLAYER_INVENTORY:
10560 for (i = 0; i < MAX_PLAYERS; i++)
10562 struct PlayerInfo *player = &stored_player[i];
10565 if (trigger_player_bits & (1 << i))
10567 int inventory_element = action_arg_element;
10569 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10570 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10571 action_arg == CA_ARG_ELEMENT_ACTION)
10573 int element = inventory_element;
10574 int collect_count = element_info[element].collect_count_initial;
10576 if (!IS_CUSTOM_ELEMENT(element))
10579 if (collect_count == 0)
10580 player->inventory_infinite_element = element;
10582 for (k = 0; k < collect_count; k++)
10583 if (player->inventory_size < MAX_INVENTORY_SIZE)
10584 player->inventory_element[player->inventory_size++] =
10587 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10588 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10589 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10591 if (player->inventory_infinite_element != EL_UNDEFINED &&
10592 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10593 action_arg_element_raw))
10594 player->inventory_infinite_element = EL_UNDEFINED;
10596 for (k = 0, j = 0; j < player->inventory_size; j++)
10598 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10599 action_arg_element_raw))
10600 player->inventory_element[k++] = player->inventory_element[j];
10603 player->inventory_size = k;
10605 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10607 if (player->inventory_size > 0)
10609 for (j = 0; j < player->inventory_size - 1; j++)
10610 player->inventory_element[j] = player->inventory_element[j + 1];
10612 player->inventory_size--;
10615 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10617 if (player->inventory_size > 0)
10618 player->inventory_size--;
10620 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10622 player->inventory_infinite_element = EL_UNDEFINED;
10623 player->inventory_size = 0;
10625 else if (action_arg == CA_ARG_INVENTORY_RESET)
10627 player->inventory_infinite_element = EL_UNDEFINED;
10628 player->inventory_size = 0;
10630 if (level.use_initial_inventory[i])
10632 for (j = 0; j < level.initial_inventory_size[i]; j++)
10634 int element = level.initial_inventory_content[i][j];
10635 int collect_count = element_info[element].collect_count_initial;
10637 if (!IS_CUSTOM_ELEMENT(element))
10640 if (collect_count == 0)
10641 player->inventory_infinite_element = element;
10643 for (k = 0; k < collect_count; k++)
10644 if (player->inventory_size < MAX_INVENTORY_SIZE)
10645 player->inventory_element[player->inventory_size++] =
10656 /* ---------- CE actions ---------------------------------------------- */
10658 case CA_SET_CE_VALUE:
10660 #if USE_NEW_CUSTOM_VALUE
10661 int last_ce_value = CustomValue[x][y];
10663 CustomValue[x][y] = action_arg_number_new;
10665 if (CustomValue[x][y] != last_ce_value)
10667 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10668 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10670 if (CustomValue[x][y] == 0)
10672 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10673 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10681 case CA_SET_CE_SCORE:
10683 #if USE_NEW_CUSTOM_VALUE
10684 int last_ce_score = ei->collect_score;
10686 ei->collect_score = action_arg_number_new;
10688 if (ei->collect_score != last_ce_score)
10690 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10691 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10693 if (ei->collect_score == 0)
10697 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10698 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10701 This is a very special case that seems to be a mixture between
10702 CheckElementChange() and CheckTriggeredElementChange(): while
10703 the first one only affects single elements that are triggered
10704 directly, the second one affects multiple elements in the playfield
10705 that are triggered indirectly by another element. This is a third
10706 case: Changing the CE score always affects multiple identical CEs,
10707 so every affected CE must be checked, not only the single CE for
10708 which the CE score was changed in the first place (as every instance
10709 of that CE shares the same CE score, and therefore also can change)!
10711 SCAN_PLAYFIELD(xx, yy)
10713 if (Feld[xx][yy] == element)
10714 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10715 CE_SCORE_GETS_ZERO);
10724 case CA_SET_CE_ARTWORK:
10726 int artwork_element = action_arg_element;
10727 boolean reset_frame = FALSE;
10730 if (action_arg == CA_ARG_ELEMENT_RESET)
10731 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10734 if (ei->gfx_element != artwork_element)
10735 reset_frame = TRUE;
10737 ei->gfx_element = artwork_element;
10739 SCAN_PLAYFIELD(xx, yy)
10741 if (Feld[xx][yy] == element)
10745 ResetGfxAnimation(xx, yy);
10746 ResetRandomAnimationValue(xx, yy);
10749 TEST_DrawLevelField(xx, yy);
10756 /* ---------- engine actions ------------------------------------------ */
10758 case CA_SET_ENGINE_SCAN_MODE:
10760 InitPlayfieldScanMode(action_arg);
10770 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10772 int old_element = Feld[x][y];
10773 int new_element = GetElementFromGroupElement(element);
10774 int previous_move_direction = MovDir[x][y];
10775 #if USE_NEW_CUSTOM_VALUE
10776 int last_ce_value = CustomValue[x][y];
10778 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10779 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10780 boolean add_player_onto_element = (new_element_is_player &&
10781 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10782 /* this breaks SnakeBite when a snake is
10783 halfway through a door that closes */
10784 /* NOW FIXED AT LEVEL INIT IN files.c */
10785 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10787 IS_WALKABLE(old_element));
10790 /* check if element under the player changes from accessible to unaccessible
10791 (needed for special case of dropping element which then changes) */
10792 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10793 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10801 if (!add_player_onto_element)
10803 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10804 RemoveMovingField(x, y);
10808 Feld[x][y] = new_element;
10810 #if !USE_GFX_RESET_GFX_ANIMATION
10811 ResetGfxAnimation(x, y);
10812 ResetRandomAnimationValue(x, y);
10815 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10816 MovDir[x][y] = previous_move_direction;
10818 #if USE_NEW_CUSTOM_VALUE
10819 if (element_info[new_element].use_last_ce_value)
10820 CustomValue[x][y] = last_ce_value;
10823 InitField_WithBug1(x, y, FALSE);
10825 new_element = Feld[x][y]; /* element may have changed */
10827 #if USE_GFX_RESET_GFX_ANIMATION
10828 ResetGfxAnimation(x, y);
10829 ResetRandomAnimationValue(x, y);
10832 TEST_DrawLevelField(x, y);
10834 if (GFX_CRUMBLED(new_element))
10835 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10839 /* check if element under the player changes from accessible to unaccessible
10840 (needed for special case of dropping element which then changes) */
10841 /* (must be checked after creating new element for walkable group elements) */
10842 #if USE_FIX_KILLED_BY_NON_WALKABLE
10843 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10844 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10851 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10852 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10861 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10862 if (new_element_is_player)
10863 RelocatePlayer(x, y, new_element);
10866 ChangeCount[x][y]++; /* count number of changes in the same frame */
10868 TestIfBadThingTouchesPlayer(x, y);
10869 TestIfPlayerTouchesCustomElement(x, y);
10870 TestIfElementTouchesCustomElement(x, y);
10873 static void CreateField(int x, int y, int element)
10875 CreateFieldExt(x, y, element, FALSE);
10878 static void CreateElementFromChange(int x, int y, int element)
10880 element = GET_VALID_RUNTIME_ELEMENT(element);
10882 #if USE_STOP_CHANGED_ELEMENTS
10883 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10885 int old_element = Feld[x][y];
10887 /* prevent changed element from moving in same engine frame
10888 unless both old and new element can either fall or move */
10889 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10890 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10895 CreateFieldExt(x, y, element, TRUE);
10898 static boolean ChangeElement(int x, int y, int element, int page)
10900 struct ElementInfo *ei = &element_info[element];
10901 struct ElementChangeInfo *change = &ei->change_page[page];
10902 int ce_value = CustomValue[x][y];
10903 int ce_score = ei->collect_score;
10904 int target_element;
10905 int old_element = Feld[x][y];
10907 /* always use default change event to prevent running into a loop */
10908 if (ChangeEvent[x][y] == -1)
10909 ChangeEvent[x][y] = CE_DELAY;
10911 if (ChangeEvent[x][y] == CE_DELAY)
10913 /* reset actual trigger element, trigger player and action element */
10914 change->actual_trigger_element = EL_EMPTY;
10915 change->actual_trigger_player = EL_EMPTY;
10916 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10917 change->actual_trigger_side = CH_SIDE_NONE;
10918 change->actual_trigger_ce_value = 0;
10919 change->actual_trigger_ce_score = 0;
10922 /* do not change elements more than a specified maximum number of changes */
10923 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10926 ChangeCount[x][y]++; /* count number of changes in the same frame */
10928 if (change->explode)
10935 if (change->use_target_content)
10937 boolean complete_replace = TRUE;
10938 boolean can_replace[3][3];
10941 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10944 boolean is_walkable;
10945 boolean is_diggable;
10946 boolean is_collectible;
10947 boolean is_removable;
10948 boolean is_destructible;
10949 int ex = x + xx - 1;
10950 int ey = y + yy - 1;
10951 int content_element = change->target_content.e[xx][yy];
10954 can_replace[xx][yy] = TRUE;
10956 if (ex == x && ey == y) /* do not check changing element itself */
10959 if (content_element == EL_EMPTY_SPACE)
10961 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10966 if (!IN_LEV_FIELD(ex, ey))
10968 can_replace[xx][yy] = FALSE;
10969 complete_replace = FALSE;
10976 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10977 e = MovingOrBlocked2Element(ex, ey);
10979 is_empty = (IS_FREE(ex, ey) ||
10980 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10982 is_walkable = (is_empty || IS_WALKABLE(e));
10983 is_diggable = (is_empty || IS_DIGGABLE(e));
10984 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10985 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10986 is_removable = (is_diggable || is_collectible);
10988 can_replace[xx][yy] =
10989 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10990 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10991 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10992 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10993 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10994 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10995 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10997 if (!can_replace[xx][yy])
10998 complete_replace = FALSE;
11001 if (!change->only_if_complete || complete_replace)
11003 boolean something_has_changed = FALSE;
11005 if (change->only_if_complete && change->use_random_replace &&
11006 RND(100) < change->random_percentage)
11009 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11011 int ex = x + xx - 1;
11012 int ey = y + yy - 1;
11013 int content_element;
11015 if (can_replace[xx][yy] && (!change->use_random_replace ||
11016 RND(100) < change->random_percentage))
11018 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11019 RemoveMovingField(ex, ey);
11021 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11023 content_element = change->target_content.e[xx][yy];
11024 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11025 ce_value, ce_score);
11027 CreateElementFromChange(ex, ey, target_element);
11029 something_has_changed = TRUE;
11031 /* for symmetry reasons, freeze newly created border elements */
11032 if (ex != x || ey != y)
11033 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11037 if (something_has_changed)
11039 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11040 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11046 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11047 ce_value, ce_score);
11049 if (element == EL_DIAGONAL_GROWING ||
11050 element == EL_DIAGONAL_SHRINKING)
11052 target_element = Store[x][y];
11054 Store[x][y] = EL_EMPTY;
11057 CreateElementFromChange(x, y, target_element);
11059 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11060 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11063 /* this uses direct change before indirect change */
11064 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11069 #if USE_NEW_DELAYED_ACTION
11071 static void HandleElementChange(int x, int y, int page)
11073 int element = MovingOrBlocked2Element(x, y);
11074 struct ElementInfo *ei = &element_info[element];
11075 struct ElementChangeInfo *change = &ei->change_page[page];
11078 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11079 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11082 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11083 x, y, element, element_info[element].token_name);
11084 printf("HandleElementChange(): This should never happen!\n");
11089 /* this can happen with classic bombs on walkable, changing elements */
11090 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11093 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11094 ChangeDelay[x][y] = 0;
11100 if (ChangeDelay[x][y] == 0) /* initialize element change */
11102 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11104 if (change->can_change)
11107 /* !!! not clear why graphic animation should be reset at all here !!! */
11108 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11109 #if USE_GFX_RESET_WHEN_NOT_MOVING
11110 /* when a custom element is about to change (for example by change delay),
11111 do not reset graphic animation when the custom element is moving */
11112 if (!IS_MOVING(x, y))
11115 ResetGfxAnimation(x, y);
11116 ResetRandomAnimationValue(x, y);
11120 if (change->pre_change_function)
11121 change->pre_change_function(x, y);
11125 ChangeDelay[x][y]--;
11127 if (ChangeDelay[x][y] != 0) /* continue element change */
11129 if (change->can_change)
11131 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11133 if (IS_ANIMATED(graphic))
11134 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11136 if (change->change_function)
11137 change->change_function(x, y);
11140 else /* finish element change */
11142 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11144 page = ChangePage[x][y];
11145 ChangePage[x][y] = -1;
11147 change = &ei->change_page[page];
11150 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11152 ChangeDelay[x][y] = 1; /* try change after next move step */
11153 ChangePage[x][y] = page; /* remember page to use for change */
11158 if (change->can_change)
11160 if (ChangeElement(x, y, element, page))
11162 if (change->post_change_function)
11163 change->post_change_function(x, y);
11167 if (change->has_action)
11168 ExecuteCustomElementAction(x, y, element, page);
11174 static void HandleElementChange(int x, int y, int page)
11176 int element = MovingOrBlocked2Element(x, y);
11177 struct ElementInfo *ei = &element_info[element];
11178 struct ElementChangeInfo *change = &ei->change_page[page];
11181 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11184 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11185 x, y, element, element_info[element].token_name);
11186 printf("HandleElementChange(): This should never happen!\n");
11191 /* this can happen with classic bombs on walkable, changing elements */
11192 if (!CAN_CHANGE(element))
11195 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11196 ChangeDelay[x][y] = 0;
11202 if (ChangeDelay[x][y] == 0) /* initialize element change */
11204 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11206 ResetGfxAnimation(x, y);
11207 ResetRandomAnimationValue(x, y);
11209 if (change->pre_change_function)
11210 change->pre_change_function(x, y);
11213 ChangeDelay[x][y]--;
11215 if (ChangeDelay[x][y] != 0) /* continue element change */
11217 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11219 if (IS_ANIMATED(graphic))
11220 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11222 if (change->change_function)
11223 change->change_function(x, y);
11225 else /* finish element change */
11227 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11229 page = ChangePage[x][y];
11230 ChangePage[x][y] = -1;
11232 change = &ei->change_page[page];
11235 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11237 ChangeDelay[x][y] = 1; /* try change after next move step */
11238 ChangePage[x][y] = page; /* remember page to use for change */
11243 if (ChangeElement(x, y, element, page))
11245 if (change->post_change_function)
11246 change->post_change_function(x, y);
11253 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11254 int trigger_element,
11256 int trigger_player,
11260 boolean change_done_any = FALSE;
11261 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11264 if (!(trigger_events[trigger_element][trigger_event]))
11268 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11269 trigger_event, recursion_loop_depth, recursion_loop_detected,
11270 recursion_loop_element, EL_NAME(recursion_loop_element));
11273 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11275 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11277 int element = EL_CUSTOM_START + i;
11278 boolean change_done = FALSE;
11281 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11282 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11285 for (p = 0; p < element_info[element].num_change_pages; p++)
11287 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11289 if (change->can_change_or_has_action &&
11290 change->has_event[trigger_event] &&
11291 change->trigger_side & trigger_side &&
11292 change->trigger_player & trigger_player &&
11293 change->trigger_page & trigger_page_bits &&
11294 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11296 change->actual_trigger_element = trigger_element;
11297 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11298 change->actual_trigger_player_bits = trigger_player;
11299 change->actual_trigger_side = trigger_side;
11300 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11301 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11304 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11305 element, EL_NAME(element), p);
11308 if ((change->can_change && !change_done) || change->has_action)
11312 SCAN_PLAYFIELD(x, y)
11314 if (Feld[x][y] == element)
11316 if (change->can_change && !change_done)
11318 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11319 /* if element already changed in this frame, not only prevent
11320 another element change (checked in ChangeElement()), but
11321 also prevent additional element actions for this element */
11323 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11324 !level.use_action_after_change_bug)
11329 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11330 element, EL_NAME(element), p);
11333 ChangeDelay[x][y] = 1;
11334 ChangeEvent[x][y] = trigger_event;
11336 HandleElementChange(x, y, p);
11338 #if USE_NEW_DELAYED_ACTION
11339 else if (change->has_action)
11341 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11342 /* if element already changed in this frame, not only prevent
11343 another element change (checked in ChangeElement()), but
11344 also prevent additional element actions for this element */
11346 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11347 !level.use_action_after_change_bug)
11353 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11354 element, EL_NAME(element), p);
11357 ExecuteCustomElementAction(x, y, element, p);
11358 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11361 if (change->has_action)
11363 ExecuteCustomElementAction(x, y, element, p);
11364 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11370 if (change->can_change)
11372 change_done = TRUE;
11373 change_done_any = TRUE;
11376 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11377 element, EL_NAME(element), p);
11386 RECURSION_LOOP_DETECTION_END();
11388 return change_done_any;
11391 static boolean CheckElementChangeExt(int x, int y,
11393 int trigger_element,
11395 int trigger_player,
11398 boolean change_done = FALSE;
11401 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11402 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11405 if (Feld[x][y] == EL_BLOCKED)
11407 Blocked2Moving(x, y, &x, &y);
11408 element = Feld[x][y];
11412 /* check if element has already changed */
11413 if (Feld[x][y] != element)
11416 /* check if element has already changed or is about to change after moving */
11417 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11418 Feld[x][y] != element) ||
11420 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11421 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11422 ChangePage[x][y] != -1)))
11427 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11428 trigger_event, recursion_loop_depth, recursion_loop_detected,
11429 recursion_loop_element, EL_NAME(recursion_loop_element));
11432 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11435 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11438 for (p = 0; p < element_info[element].num_change_pages; p++)
11440 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11442 /* check trigger element for all events where the element that is checked
11443 for changing interacts with a directly adjacent element -- this is
11444 different to element changes that affect other elements to change on the
11445 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11446 boolean check_trigger_element =
11447 (trigger_event == CE_TOUCHING_X ||
11448 trigger_event == CE_HITTING_X ||
11449 trigger_event == CE_HIT_BY_X ||
11451 /* this one was forgotten until 3.2.3 */
11452 trigger_event == CE_DIGGING_X);
11455 if (change->can_change_or_has_action &&
11456 change->has_event[trigger_event] &&
11457 change->trigger_side & trigger_side &&
11458 change->trigger_player & trigger_player &&
11459 (!check_trigger_element ||
11460 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11462 change->actual_trigger_element = trigger_element;
11463 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11464 change->actual_trigger_player_bits = trigger_player;
11465 change->actual_trigger_side = trigger_side;
11466 change->actual_trigger_ce_value = CustomValue[x][y];
11467 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11469 /* special case: trigger element not at (x,y) position for some events */
11470 if (check_trigger_element)
11482 { 0, 0 }, { 0, 0 }, { 0, 0 },
11486 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11487 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11489 change->actual_trigger_ce_value = CustomValue[xx][yy];
11490 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11493 if (change->can_change && !change_done)
11495 ChangeDelay[x][y] = 1;
11496 ChangeEvent[x][y] = trigger_event;
11498 HandleElementChange(x, y, p);
11500 change_done = TRUE;
11502 #if USE_NEW_DELAYED_ACTION
11503 else if (change->has_action)
11505 ExecuteCustomElementAction(x, y, element, p);
11506 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11509 if (change->has_action)
11511 ExecuteCustomElementAction(x, y, element, p);
11512 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11518 RECURSION_LOOP_DETECTION_END();
11520 return change_done;
11523 static void PlayPlayerSound(struct PlayerInfo *player)
11525 int jx = player->jx, jy = player->jy;
11526 int sound_element = player->artwork_element;
11527 int last_action = player->last_action_waiting;
11528 int action = player->action_waiting;
11530 if (player->is_waiting)
11532 if (action != last_action)
11533 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11535 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11539 if (action != last_action)
11540 StopSound(element_info[sound_element].sound[last_action]);
11542 if (last_action == ACTION_SLEEPING)
11543 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11547 static void PlayAllPlayersSound()
11551 for (i = 0; i < MAX_PLAYERS; i++)
11552 if (stored_player[i].active)
11553 PlayPlayerSound(&stored_player[i]);
11556 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11558 boolean last_waiting = player->is_waiting;
11559 int move_dir = player->MovDir;
11561 player->dir_waiting = move_dir;
11562 player->last_action_waiting = player->action_waiting;
11566 if (!last_waiting) /* not waiting -> waiting */
11568 player->is_waiting = TRUE;
11570 player->frame_counter_bored =
11572 game.player_boring_delay_fixed +
11573 GetSimpleRandom(game.player_boring_delay_random);
11574 player->frame_counter_sleeping =
11576 game.player_sleeping_delay_fixed +
11577 GetSimpleRandom(game.player_sleeping_delay_random);
11579 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11582 if (game.player_sleeping_delay_fixed +
11583 game.player_sleeping_delay_random > 0 &&
11584 player->anim_delay_counter == 0 &&
11585 player->post_delay_counter == 0 &&
11586 FrameCounter >= player->frame_counter_sleeping)
11587 player->is_sleeping = TRUE;
11588 else if (game.player_boring_delay_fixed +
11589 game.player_boring_delay_random > 0 &&
11590 FrameCounter >= player->frame_counter_bored)
11591 player->is_bored = TRUE;
11593 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11594 player->is_bored ? ACTION_BORING :
11597 if (player->is_sleeping && player->use_murphy)
11599 /* special case for sleeping Murphy when leaning against non-free tile */
11601 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11602 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11603 !IS_MOVING(player->jx - 1, player->jy)))
11604 move_dir = MV_LEFT;
11605 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11606 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11607 !IS_MOVING(player->jx + 1, player->jy)))
11608 move_dir = MV_RIGHT;
11610 player->is_sleeping = FALSE;
11612 player->dir_waiting = move_dir;
11615 if (player->is_sleeping)
11617 if (player->num_special_action_sleeping > 0)
11619 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11621 int last_special_action = player->special_action_sleeping;
11622 int num_special_action = player->num_special_action_sleeping;
11623 int special_action =
11624 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11625 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11626 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11627 last_special_action + 1 : ACTION_SLEEPING);
11628 int special_graphic =
11629 el_act_dir2img(player->artwork_element, special_action, move_dir);
11631 player->anim_delay_counter =
11632 graphic_info[special_graphic].anim_delay_fixed +
11633 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11634 player->post_delay_counter =
11635 graphic_info[special_graphic].post_delay_fixed +
11636 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11638 player->special_action_sleeping = special_action;
11641 if (player->anim_delay_counter > 0)
11643 player->action_waiting = player->special_action_sleeping;
11644 player->anim_delay_counter--;
11646 else if (player->post_delay_counter > 0)
11648 player->post_delay_counter--;
11652 else if (player->is_bored)
11654 if (player->num_special_action_bored > 0)
11656 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11658 int special_action =
11659 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11660 int special_graphic =
11661 el_act_dir2img(player->artwork_element, special_action, move_dir);
11663 player->anim_delay_counter =
11664 graphic_info[special_graphic].anim_delay_fixed +
11665 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11666 player->post_delay_counter =
11667 graphic_info[special_graphic].post_delay_fixed +
11668 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11670 player->special_action_bored = special_action;
11673 if (player->anim_delay_counter > 0)
11675 player->action_waiting = player->special_action_bored;
11676 player->anim_delay_counter--;
11678 else if (player->post_delay_counter > 0)
11680 player->post_delay_counter--;
11685 else if (last_waiting) /* waiting -> not waiting */
11687 player->is_waiting = FALSE;
11688 player->is_bored = FALSE;
11689 player->is_sleeping = FALSE;
11691 player->frame_counter_bored = -1;
11692 player->frame_counter_sleeping = -1;
11694 player->anim_delay_counter = 0;
11695 player->post_delay_counter = 0;
11697 player->dir_waiting = player->MovDir;
11698 player->action_waiting = ACTION_DEFAULT;
11700 player->special_action_bored = ACTION_DEFAULT;
11701 player->special_action_sleeping = ACTION_DEFAULT;
11705 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11707 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11708 int left = player_action & JOY_LEFT;
11709 int right = player_action & JOY_RIGHT;
11710 int up = player_action & JOY_UP;
11711 int down = player_action & JOY_DOWN;
11712 int button1 = player_action & JOY_BUTTON_1;
11713 int button2 = player_action & JOY_BUTTON_2;
11714 int dx = (left ? -1 : right ? 1 : 0);
11715 int dy = (up ? -1 : down ? 1 : 0);
11717 if (!player->active || tape.pausing)
11723 snapped = SnapField(player, dx, dy);
11727 dropped = DropElement(player);
11729 moved = MovePlayer(player, dx, dy);
11732 if (tape.single_step && tape.recording && !tape.pausing)
11734 if (button1 || (dropped && !moved))
11736 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11737 SnapField(player, 0, 0); /* stop snapping */
11741 SetPlayerWaiting(player, FALSE);
11743 return player_action;
11747 /* no actions for this player (no input at player's configured device) */
11749 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11750 SnapField(player, 0, 0);
11751 CheckGravityMovementWhenNotMoving(player);
11753 if (player->MovPos == 0)
11754 SetPlayerWaiting(player, TRUE);
11756 if (player->MovPos == 0) /* needed for tape.playing */
11757 player->is_moving = FALSE;
11759 player->is_dropping = FALSE;
11760 player->is_dropping_pressed = FALSE;
11761 player->drop_pressed_delay = 0;
11767 static void CheckLevelTime()
11771 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11773 if (level.native_em_level->lev->home == 0) /* all players at home */
11775 PlayerWins(local_player);
11777 AllPlayersGone = TRUE;
11779 level.native_em_level->lev->home = -1;
11782 if (level.native_em_level->ply[0]->alive == 0 &&
11783 level.native_em_level->ply[1]->alive == 0 &&
11784 level.native_em_level->ply[2]->alive == 0 &&
11785 level.native_em_level->ply[3]->alive == 0) /* all dead */
11786 AllPlayersGone = TRUE;
11789 if (TimeFrames >= FRAMES_PER_SECOND)
11794 for (i = 0; i < MAX_PLAYERS; i++)
11796 struct PlayerInfo *player = &stored_player[i];
11798 if (SHIELD_ON(player))
11800 player->shield_normal_time_left--;
11802 if (player->shield_deadly_time_left > 0)
11803 player->shield_deadly_time_left--;
11807 if (!local_player->LevelSolved && !level.use_step_counter)
11815 if (TimeLeft <= 10 && setup.time_limit)
11816 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11819 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11821 DisplayGameControlValues();
11823 DrawGameValue_Time(TimeLeft);
11826 if (!TimeLeft && setup.time_limit)
11828 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11829 level.native_em_level->lev->killed_out_of_time = TRUE;
11831 for (i = 0; i < MAX_PLAYERS; i++)
11832 KillPlayer(&stored_player[i]);
11836 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11838 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11840 DisplayGameControlValues();
11843 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11844 DrawGameValue_Time(TimePlayed);
11847 level.native_em_level->lev->time =
11848 (level.time == 0 ? TimePlayed : TimeLeft);
11851 if (tape.recording || tape.playing)
11852 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11856 UpdateAndDisplayGameControlValues();
11858 UpdateGameDoorValues();
11859 DrawGameDoorValues();
11863 void AdvanceFrameAndPlayerCounters(int player_nr)
11867 /* advance frame counters (global frame counter and time frame counter) */
11871 /* advance player counters (counters for move delay, move animation etc.) */
11872 for (i = 0; i < MAX_PLAYERS; i++)
11874 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11875 int move_delay_value = stored_player[i].move_delay_value;
11876 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11878 if (!advance_player_counters) /* not all players may be affected */
11881 #if USE_NEW_PLAYER_ANIM
11882 if (move_frames == 0) /* less than one move per game frame */
11884 int stepsize = TILEX / move_delay_value;
11885 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11886 int count = (stored_player[i].is_moving ?
11887 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11889 if (count % delay == 0)
11894 stored_player[i].Frame += move_frames;
11896 if (stored_player[i].MovPos != 0)
11897 stored_player[i].StepFrame += move_frames;
11899 if (stored_player[i].move_delay > 0)
11900 stored_player[i].move_delay--;
11902 /* due to bugs in previous versions, counter must count up, not down */
11903 if (stored_player[i].push_delay != -1)
11904 stored_player[i].push_delay++;
11906 if (stored_player[i].drop_delay > 0)
11907 stored_player[i].drop_delay--;
11909 if (stored_player[i].is_dropping_pressed)
11910 stored_player[i].drop_pressed_delay++;
11914 void StartGameActions(boolean init_network_game, boolean record_tape,
11917 unsigned long new_random_seed = InitRND(random_seed);
11920 TapeStartRecording(new_random_seed);
11922 #if defined(NETWORK_AVALIABLE)
11923 if (init_network_game)
11925 SendToServer_StartPlaying();
11936 static unsigned long game_frame_delay = 0;
11937 unsigned long game_frame_delay_value;
11938 byte *recorded_player_action;
11939 byte summarized_player_action = 0;
11940 byte tape_action[MAX_PLAYERS];
11943 /* detect endless loops, caused by custom element programming */
11944 if (recursion_loop_detected && recursion_loop_depth == 0)
11946 char *message = getStringCat3("Internal Error ! Element ",
11947 EL_NAME(recursion_loop_element),
11948 " caused endless loop ! Quit the game ?");
11950 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11951 EL_NAME(recursion_loop_element));
11953 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11955 recursion_loop_detected = FALSE; /* if game should be continued */
11962 if (game.restart_level)
11963 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11965 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11967 if (level.native_em_level->lev->home == 0) /* all players at home */
11969 PlayerWins(local_player);
11971 AllPlayersGone = TRUE;
11973 level.native_em_level->lev->home = -1;
11976 if (level.native_em_level->ply[0]->alive == 0 &&
11977 level.native_em_level->ply[1]->alive == 0 &&
11978 level.native_em_level->ply[2]->alive == 0 &&
11979 level.native_em_level->ply[3]->alive == 0) /* all dead */
11980 AllPlayersGone = TRUE;
11983 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11986 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11989 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11992 game_frame_delay_value =
11993 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11995 if (tape.playing && tape.warp_forward && !tape.pausing)
11996 game_frame_delay_value = 0;
11998 /* ---------- main game synchronization point ---------- */
12000 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12002 if (network_playing && !network_player_action_received)
12004 /* try to get network player actions in time */
12006 #if defined(NETWORK_AVALIABLE)
12007 /* last chance to get network player actions without main loop delay */
12008 HandleNetworking();
12011 /* game was quit by network peer */
12012 if (game_status != GAME_MODE_PLAYING)
12015 if (!network_player_action_received)
12016 return; /* failed to get network player actions in time */
12018 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12024 /* at this point we know that we really continue executing the game */
12026 network_player_action_received = FALSE;
12028 /* when playing tape, read previously recorded player input from tape data */
12029 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12032 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12037 if (tape.set_centered_player)
12039 game.centered_player_nr_next = tape.centered_player_nr_next;
12040 game.set_centered_player = TRUE;
12043 for (i = 0; i < MAX_PLAYERS; i++)
12045 summarized_player_action |= stored_player[i].action;
12047 if (!network_playing)
12048 stored_player[i].effective_action = stored_player[i].action;
12051 #if defined(NETWORK_AVALIABLE)
12052 if (network_playing)
12053 SendToServer_MovePlayer(summarized_player_action);
12056 if (!options.network && !setup.team_mode)
12057 local_player->effective_action = summarized_player_action;
12059 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12061 for (i = 0; i < MAX_PLAYERS; i++)
12062 stored_player[i].effective_action =
12063 (i == game.centered_player_nr ? summarized_player_action : 0);
12066 if (recorded_player_action != NULL)
12067 for (i = 0; i < MAX_PLAYERS; i++)
12068 stored_player[i].effective_action = recorded_player_action[i];
12070 for (i = 0; i < MAX_PLAYERS; i++)
12072 tape_action[i] = stored_player[i].effective_action;
12074 /* (this can only happen in the R'n'D game engine) */
12075 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12076 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12079 /* only record actions from input devices, but not programmed actions */
12080 if (tape.recording)
12081 TapeRecordAction(tape_action);
12083 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12085 GameActions_EM_Main();
12093 void GameActions_EM_Main()
12095 byte effective_action[MAX_PLAYERS];
12096 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12099 for (i = 0; i < MAX_PLAYERS; i++)
12100 effective_action[i] = stored_player[i].effective_action;
12102 GameActions_EM(effective_action, warp_mode);
12106 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12109 void GameActions_RND()
12111 int magic_wall_x = 0, magic_wall_y = 0;
12112 int i, x, y, element, graphic;
12114 InitPlayfieldScanModeVars();
12116 #if USE_ONE_MORE_CHANGE_PER_FRAME
12117 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12119 SCAN_PLAYFIELD(x, y)
12121 ChangeCount[x][y] = 0;
12122 ChangeEvent[x][y] = -1;
12127 if (game.set_centered_player)
12129 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12131 /* switching to "all players" only possible if all players fit to screen */
12132 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12134 game.centered_player_nr_next = game.centered_player_nr;
12135 game.set_centered_player = FALSE;
12138 /* do not switch focus to non-existing (or non-active) player */
12139 if (game.centered_player_nr_next >= 0 &&
12140 !stored_player[game.centered_player_nr_next].active)
12142 game.centered_player_nr_next = game.centered_player_nr;
12143 game.set_centered_player = FALSE;
12147 if (game.set_centered_player &&
12148 ScreenMovPos == 0) /* screen currently aligned at tile position */
12152 if (game.centered_player_nr_next == -1)
12154 setScreenCenteredToAllPlayers(&sx, &sy);
12158 sx = stored_player[game.centered_player_nr_next].jx;
12159 sy = stored_player[game.centered_player_nr_next].jy;
12162 game.centered_player_nr = game.centered_player_nr_next;
12163 game.set_centered_player = FALSE;
12165 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12166 DrawGameDoorValues();
12169 for (i = 0; i < MAX_PLAYERS; i++)
12171 int actual_player_action = stored_player[i].effective_action;
12174 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12175 - rnd_equinox_tetrachloride 048
12176 - rnd_equinox_tetrachloride_ii 096
12177 - rnd_emanuel_schmieg 002
12178 - doctor_sloan_ww 001, 020
12180 if (stored_player[i].MovPos == 0)
12181 CheckGravityMovement(&stored_player[i]);
12184 /* overwrite programmed action with tape action */
12185 if (stored_player[i].programmed_action)
12186 actual_player_action = stored_player[i].programmed_action;
12188 PlayerActions(&stored_player[i], actual_player_action);
12190 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12193 ScrollScreen(NULL, SCROLL_GO_ON);
12195 /* for backwards compatibility, the following code emulates a fixed bug that
12196 occured when pushing elements (causing elements that just made their last
12197 pushing step to already (if possible) make their first falling step in the
12198 same game frame, which is bad); this code is also needed to use the famous
12199 "spring push bug" which is used in older levels and might be wanted to be
12200 used also in newer levels, but in this case the buggy pushing code is only
12201 affecting the "spring" element and no other elements */
12203 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12205 for (i = 0; i < MAX_PLAYERS; i++)
12207 struct PlayerInfo *player = &stored_player[i];
12208 int x = player->jx;
12209 int y = player->jy;
12211 if (player->active && player->is_pushing && player->is_moving &&
12213 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12214 Feld[x][y] == EL_SPRING))
12216 ContinueMoving(x, y);
12218 /* continue moving after pushing (this is actually a bug) */
12219 if (!IS_MOVING(x, y))
12220 Stop[x][y] = FALSE;
12226 debug_print_timestamp(0, "start main loop profiling");
12229 SCAN_PLAYFIELD(x, y)
12231 ChangeCount[x][y] = 0;
12232 ChangeEvent[x][y] = -1;
12234 /* this must be handled before main playfield loop */
12235 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12238 if (MovDelay[x][y] <= 0)
12242 #if USE_NEW_SNAP_DELAY
12243 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12246 if (MovDelay[x][y] <= 0)
12249 TEST_DrawLevelField(x, y);
12251 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12257 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12259 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12260 printf("GameActions(): This should never happen!\n");
12262 ChangePage[x][y] = -1;
12266 Stop[x][y] = FALSE;
12267 if (WasJustMoving[x][y] > 0)
12268 WasJustMoving[x][y]--;
12269 if (WasJustFalling[x][y] > 0)
12270 WasJustFalling[x][y]--;
12271 if (CheckCollision[x][y] > 0)
12272 CheckCollision[x][y]--;
12273 if (CheckImpact[x][y] > 0)
12274 CheckImpact[x][y]--;
12278 /* reset finished pushing action (not done in ContinueMoving() to allow
12279 continuous pushing animation for elements with zero push delay) */
12280 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12282 ResetGfxAnimation(x, y);
12283 TEST_DrawLevelField(x, y);
12287 if (IS_BLOCKED(x, y))
12291 Blocked2Moving(x, y, &oldx, &oldy);
12292 if (!IS_MOVING(oldx, oldy))
12294 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12295 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12296 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12297 printf("GameActions(): This should never happen!\n");
12304 debug_print_timestamp(0, "- time for pre-main loop:");
12307 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12308 SCAN_PLAYFIELD(x, y)
12310 element = Feld[x][y];
12311 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12316 int element2 = element;
12317 int graphic2 = graphic;
12319 int element2 = Feld[x][y];
12320 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12322 int last_gfx_frame = GfxFrame[x][y];
12324 if (graphic_info[graphic2].anim_global_sync)
12325 GfxFrame[x][y] = FrameCounter;
12326 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12327 GfxFrame[x][y] = CustomValue[x][y];
12328 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12329 GfxFrame[x][y] = element_info[element2].collect_score;
12330 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12331 GfxFrame[x][y] = ChangeDelay[x][y];
12333 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12334 DrawLevelGraphicAnimation(x, y, graphic2);
12337 ResetGfxFrame(x, y, TRUE);
12341 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12342 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12343 ResetRandomAnimationValue(x, y);
12347 SetRandomAnimationValue(x, y);
12351 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12354 #endif // -------------------- !!! TEST ONLY !!! --------------------
12357 debug_print_timestamp(0, "- time for TEST loop: -->");
12360 SCAN_PLAYFIELD(x, y)
12362 element = Feld[x][y];
12363 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12365 ResetGfxFrame(x, y, TRUE);
12367 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12368 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12369 ResetRandomAnimationValue(x, y);
12371 SetRandomAnimationValue(x, y);
12373 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12375 if (IS_INACTIVE(element))
12377 if (IS_ANIMATED(graphic))
12378 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12383 /* this may take place after moving, so 'element' may have changed */
12384 if (IS_CHANGING(x, y) &&
12385 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12387 int page = element_info[element].event_page_nr[CE_DELAY];
12390 HandleElementChange(x, y, page);
12392 if (CAN_CHANGE(element))
12393 HandleElementChange(x, y, page);
12395 if (HAS_ACTION(element))
12396 ExecuteCustomElementAction(x, y, element, page);
12399 element = Feld[x][y];
12400 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12403 #if 0 // ---------------------------------------------------------------------
12405 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12409 element = Feld[x][y];
12410 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12412 if (IS_ANIMATED(graphic) &&
12413 !IS_MOVING(x, y) &&
12415 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12417 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12418 TEST_DrawTwinkleOnField(x, y);
12420 else if (IS_MOVING(x, y))
12421 ContinueMoving(x, y);
12428 case EL_EM_EXIT_OPEN:
12429 case EL_SP_EXIT_OPEN:
12430 case EL_STEEL_EXIT_OPEN:
12431 case EL_EM_STEEL_EXIT_OPEN:
12432 case EL_SP_TERMINAL:
12433 case EL_SP_TERMINAL_ACTIVE:
12434 case EL_EXTRA_TIME:
12435 case EL_SHIELD_NORMAL:
12436 case EL_SHIELD_DEADLY:
12437 if (IS_ANIMATED(graphic))
12438 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12441 case EL_DYNAMITE_ACTIVE:
12442 case EL_EM_DYNAMITE_ACTIVE:
12443 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12444 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12445 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12446 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12447 case EL_SP_DISK_RED_ACTIVE:
12448 CheckDynamite(x, y);
12451 case EL_AMOEBA_GROWING:
12452 AmoebeWaechst(x, y);
12455 case EL_AMOEBA_SHRINKING:
12456 AmoebaDisappearing(x, y);
12459 #if !USE_NEW_AMOEBA_CODE
12460 case EL_AMOEBA_WET:
12461 case EL_AMOEBA_DRY:
12462 case EL_AMOEBA_FULL:
12464 case EL_EMC_DRIPPER:
12465 AmoebeAbleger(x, y);
12469 case EL_GAME_OF_LIFE:
12474 case EL_EXIT_CLOSED:
12478 case EL_EM_EXIT_CLOSED:
12482 case EL_STEEL_EXIT_CLOSED:
12483 CheckExitSteel(x, y);
12486 case EL_EM_STEEL_EXIT_CLOSED:
12487 CheckExitSteelEM(x, y);
12490 case EL_SP_EXIT_CLOSED:
12494 case EL_EXPANDABLE_WALL_GROWING:
12495 case EL_EXPANDABLE_STEELWALL_GROWING:
12496 MauerWaechst(x, y);
12499 case EL_EXPANDABLE_WALL:
12500 case EL_EXPANDABLE_WALL_HORIZONTAL:
12501 case EL_EXPANDABLE_WALL_VERTICAL:
12502 case EL_EXPANDABLE_WALL_ANY:
12503 case EL_BD_EXPANDABLE_WALL:
12504 MauerAbleger(x, y);
12507 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12508 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12509 case EL_EXPANDABLE_STEELWALL_ANY:
12510 MauerAblegerStahl(x, y);
12514 CheckForDragon(x, y);
12520 case EL_ELEMENT_SNAPPING:
12521 case EL_DIAGONAL_SHRINKING:
12522 case EL_DIAGONAL_GROWING:
12525 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12527 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12532 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12533 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12538 #else // ---------------------------------------------------------------------
12540 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12544 element = Feld[x][y];
12545 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12547 if (IS_ANIMATED(graphic) &&
12548 !IS_MOVING(x, y) &&
12550 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12552 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12553 TEST_DrawTwinkleOnField(x, y);
12555 else if ((element == EL_ACID ||
12556 element == EL_EXIT_OPEN ||
12557 element == EL_EM_EXIT_OPEN ||
12558 element == EL_SP_EXIT_OPEN ||
12559 element == EL_STEEL_EXIT_OPEN ||
12560 element == EL_EM_STEEL_EXIT_OPEN ||
12561 element == EL_SP_TERMINAL ||
12562 element == EL_SP_TERMINAL_ACTIVE ||
12563 element == EL_EXTRA_TIME ||
12564 element == EL_SHIELD_NORMAL ||
12565 element == EL_SHIELD_DEADLY) &&
12566 IS_ANIMATED(graphic))
12567 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12568 else if (IS_MOVING(x, y))
12569 ContinueMoving(x, y);
12570 else if (IS_ACTIVE_BOMB(element))
12571 CheckDynamite(x, y);
12572 else if (element == EL_AMOEBA_GROWING)
12573 AmoebeWaechst(x, y);
12574 else if (element == EL_AMOEBA_SHRINKING)
12575 AmoebaDisappearing(x, y);
12577 #if !USE_NEW_AMOEBA_CODE
12578 else if (IS_AMOEBALIVE(element))
12579 AmoebeAbleger(x, y);
12582 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12584 else if (element == EL_EXIT_CLOSED)
12586 else if (element == EL_EM_EXIT_CLOSED)
12588 else if (element == EL_STEEL_EXIT_CLOSED)
12589 CheckExitSteel(x, y);
12590 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12591 CheckExitSteelEM(x, y);
12592 else if (element == EL_SP_EXIT_CLOSED)
12594 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12595 element == EL_EXPANDABLE_STEELWALL_GROWING)
12596 MauerWaechst(x, y);
12597 else if (element == EL_EXPANDABLE_WALL ||
12598 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12599 element == EL_EXPANDABLE_WALL_VERTICAL ||
12600 element == EL_EXPANDABLE_WALL_ANY ||
12601 element == EL_BD_EXPANDABLE_WALL)
12602 MauerAbleger(x, y);
12603 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12604 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12605 element == EL_EXPANDABLE_STEELWALL_ANY)
12606 MauerAblegerStahl(x, y);
12607 else if (element == EL_FLAMES)
12608 CheckForDragon(x, y);
12609 else if (element == EL_EXPLOSION)
12610 ; /* drawing of correct explosion animation is handled separately */
12611 else if (element == EL_ELEMENT_SNAPPING ||
12612 element == EL_DIAGONAL_SHRINKING ||
12613 element == EL_DIAGONAL_GROWING)
12615 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12619 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12620 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12622 #endif // ---------------------------------------------------------------------
12624 if (IS_BELT_ACTIVE(element))
12625 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12627 if (game.magic_wall_active)
12629 int jx = local_player->jx, jy = local_player->jy;
12631 /* play the element sound at the position nearest to the player */
12632 if ((element == EL_MAGIC_WALL_FULL ||
12633 element == EL_MAGIC_WALL_ACTIVE ||
12634 element == EL_MAGIC_WALL_EMPTYING ||
12635 element == EL_BD_MAGIC_WALL_FULL ||
12636 element == EL_BD_MAGIC_WALL_ACTIVE ||
12637 element == EL_BD_MAGIC_WALL_EMPTYING ||
12638 element == EL_DC_MAGIC_WALL_FULL ||
12639 element == EL_DC_MAGIC_WALL_ACTIVE ||
12640 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12641 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12650 debug_print_timestamp(0, "- time for MAIN loop: -->");
12653 #if USE_NEW_AMOEBA_CODE
12654 /* new experimental amoeba growth stuff */
12655 if (!(FrameCounter % 8))
12657 static unsigned long random = 1684108901;
12659 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12661 x = RND(lev_fieldx);
12662 y = RND(lev_fieldy);
12663 element = Feld[x][y];
12665 if (!IS_PLAYER(x,y) &&
12666 (element == EL_EMPTY ||
12667 CAN_GROW_INTO(element) ||
12668 element == EL_QUICKSAND_EMPTY ||
12669 element == EL_QUICKSAND_FAST_EMPTY ||
12670 element == EL_ACID_SPLASH_LEFT ||
12671 element == EL_ACID_SPLASH_RIGHT))
12673 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12674 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12675 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12676 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12677 Feld[x][y] = EL_AMOEBA_DROP;
12680 random = random * 129 + 1;
12686 if (game.explosions_delayed)
12689 game.explosions_delayed = FALSE;
12691 SCAN_PLAYFIELD(x, y)
12693 element = Feld[x][y];
12695 if (ExplodeField[x][y])
12696 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12697 else if (element == EL_EXPLOSION)
12698 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12700 ExplodeField[x][y] = EX_TYPE_NONE;
12703 game.explosions_delayed = TRUE;
12706 if (game.magic_wall_active)
12708 if (!(game.magic_wall_time_left % 4))
12710 int element = Feld[magic_wall_x][magic_wall_y];
12712 if (element == EL_BD_MAGIC_WALL_FULL ||
12713 element == EL_BD_MAGIC_WALL_ACTIVE ||
12714 element == EL_BD_MAGIC_WALL_EMPTYING)
12715 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12716 else if (element == EL_DC_MAGIC_WALL_FULL ||
12717 element == EL_DC_MAGIC_WALL_ACTIVE ||
12718 element == EL_DC_MAGIC_WALL_EMPTYING)
12719 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12721 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12724 if (game.magic_wall_time_left > 0)
12726 game.magic_wall_time_left--;
12728 if (!game.magic_wall_time_left)
12730 SCAN_PLAYFIELD(x, y)
12732 element = Feld[x][y];
12734 if (element == EL_MAGIC_WALL_ACTIVE ||
12735 element == EL_MAGIC_WALL_FULL)
12737 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12738 TEST_DrawLevelField(x, y);
12740 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12741 element == EL_BD_MAGIC_WALL_FULL)
12743 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12744 TEST_DrawLevelField(x, y);
12746 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12747 element == EL_DC_MAGIC_WALL_FULL)
12749 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12750 TEST_DrawLevelField(x, y);
12754 game.magic_wall_active = FALSE;
12759 if (game.light_time_left > 0)
12761 game.light_time_left--;
12763 if (game.light_time_left == 0)
12764 RedrawAllLightSwitchesAndInvisibleElements();
12767 if (game.timegate_time_left > 0)
12769 game.timegate_time_left--;
12771 if (game.timegate_time_left == 0)
12772 CloseAllOpenTimegates();
12775 if (game.lenses_time_left > 0)
12777 game.lenses_time_left--;
12779 if (game.lenses_time_left == 0)
12780 RedrawAllInvisibleElementsForLenses();
12783 if (game.magnify_time_left > 0)
12785 game.magnify_time_left--;
12787 if (game.magnify_time_left == 0)
12788 RedrawAllInvisibleElementsForMagnifier();
12791 for (i = 0; i < MAX_PLAYERS; i++)
12793 struct PlayerInfo *player = &stored_player[i];
12795 if (SHIELD_ON(player))
12797 if (player->shield_deadly_time_left)
12798 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12799 else if (player->shield_normal_time_left)
12800 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12804 #if USE_DELAYED_GFX_REDRAW
12805 SCAN_PLAYFIELD(x, y)
12808 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12810 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12811 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12814 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12815 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12817 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12818 DrawLevelField(x, y);
12820 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12821 DrawLevelFieldCrumbledSand(x, y);
12823 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12824 DrawLevelFieldCrumbledSandNeighbours(x, y);
12826 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12827 DrawTwinkleOnField(x, y);
12830 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12837 PlayAllPlayersSound();
12839 if (options.debug) /* calculate frames per second */
12841 static unsigned long fps_counter = 0;
12842 static int fps_frames = 0;
12843 unsigned long fps_delay_ms = Counter() - fps_counter;
12847 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12849 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12852 fps_counter = Counter();
12855 redraw_mask |= REDRAW_FPS;
12858 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12860 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12862 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12864 local_player->show_envelope = 0;
12868 debug_print_timestamp(0, "stop main loop profiling ");
12869 printf("----------------------------------------------------------\n");
12872 /* use random number generator in every frame to make it less predictable */
12873 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12877 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12879 int min_x = x, min_y = y, max_x = x, max_y = y;
12882 for (i = 0; i < MAX_PLAYERS; i++)
12884 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12886 if (!stored_player[i].active || &stored_player[i] == player)
12889 min_x = MIN(min_x, jx);
12890 min_y = MIN(min_y, jy);
12891 max_x = MAX(max_x, jx);
12892 max_y = MAX(max_y, jy);
12895 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12898 static boolean AllPlayersInVisibleScreen()
12902 for (i = 0; i < MAX_PLAYERS; i++)
12904 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12906 if (!stored_player[i].active)
12909 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12916 void ScrollLevel(int dx, int dy)
12919 /* (directly solved in BlitBitmap() now) */
12920 static Bitmap *bitmap_db_field2 = NULL;
12921 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12928 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12929 /* only horizontal XOR vertical scroll direction allowed */
12930 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12935 /* (directly solved in BlitBitmap() now) */
12936 if (bitmap_db_field2 == NULL)
12937 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12939 /* needed when blitting directly to same bitmap -- should not be needed with
12940 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12941 BlitBitmap(drawto_field, bitmap_db_field2,
12942 FX + TILEX * (dx == -1) - softscroll_offset,
12943 FY + TILEY * (dy == -1) - softscroll_offset,
12944 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12945 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12946 FX + TILEX * (dx == 1) - softscroll_offset,
12947 FY + TILEY * (dy == 1) - softscroll_offset);
12948 BlitBitmap(bitmap_db_field2, drawto_field,
12949 FX + TILEX * (dx == 1) - softscroll_offset,
12950 FY + TILEY * (dy == 1) - softscroll_offset,
12951 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12952 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12953 FX + TILEX * (dx == 1) - softscroll_offset,
12954 FY + TILEY * (dy == 1) - softscroll_offset);
12959 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12960 int xsize = (BX2 - BX1 + 1);
12961 int ysize = (BY2 - BY1 + 1);
12962 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12963 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12964 int step = (start < end ? +1 : -1);
12966 for (i = start; i != end; i += step)
12968 BlitBitmap(drawto_field, drawto_field,
12969 FX + TILEX * (dx != 0 ? i + step : 0),
12970 FY + TILEY * (dy != 0 ? i + step : 0),
12971 TILEX * (dx != 0 ? 1 : xsize),
12972 TILEY * (dy != 0 ? 1 : ysize),
12973 FX + TILEX * (dx != 0 ? i : 0),
12974 FY + TILEY * (dy != 0 ? i : 0));
12979 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12981 BlitBitmap(drawto_field, drawto_field,
12982 FX + TILEX * (dx == -1) - softscroll_offset,
12983 FY + TILEY * (dy == -1) - softscroll_offset,
12984 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12985 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12986 FX + TILEX * (dx == 1) - softscroll_offset,
12987 FY + TILEY * (dy == 1) - softscroll_offset);
12993 x = (dx == 1 ? BX1 : BX2);
12994 for (y = BY1; y <= BY2; y++)
12995 DrawScreenField(x, y);
13000 y = (dy == 1 ? BY1 : BY2);
13001 for (x = BX1; x <= BX2; x++)
13002 DrawScreenField(x, y);
13005 redraw_mask |= REDRAW_FIELD;
13008 static boolean canFallDown(struct PlayerInfo *player)
13010 int jx = player->jx, jy = player->jy;
13012 return (IN_LEV_FIELD(jx, jy + 1) &&
13013 (IS_FREE(jx, jy + 1) ||
13014 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13015 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13016 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13019 static boolean canPassField(int x, int y, int move_dir)
13021 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13022 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13023 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13024 int nextx = x + dx;
13025 int nexty = y + dy;
13026 int element = Feld[x][y];
13028 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13029 !CAN_MOVE(element) &&
13030 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13031 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13032 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13035 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13037 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13038 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13039 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13043 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13044 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13045 (IS_DIGGABLE(Feld[newx][newy]) ||
13046 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13047 canPassField(newx, newy, move_dir)));
13050 static void CheckGravityMovement(struct PlayerInfo *player)
13052 #if USE_PLAYER_GRAVITY
13053 if (player->gravity && !player->programmed_action)
13055 if (game.gravity && !player->programmed_action)
13058 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13059 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13060 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13061 int jx = player->jx, jy = player->jy;
13062 boolean player_is_moving_to_valid_field =
13063 (!player_is_snapping &&
13064 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13065 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13066 boolean player_can_fall_down = canFallDown(player);
13068 if (player_can_fall_down &&
13069 !player_is_moving_to_valid_field)
13070 player->programmed_action = MV_DOWN;
13074 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13076 return CheckGravityMovement(player);
13078 #if USE_PLAYER_GRAVITY
13079 if (player->gravity && !player->programmed_action)
13081 if (game.gravity && !player->programmed_action)
13084 int jx = player->jx, jy = player->jy;
13085 boolean field_under_player_is_free =
13086 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13087 boolean player_is_standing_on_valid_field =
13088 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13089 (IS_WALKABLE(Feld[jx][jy]) &&
13090 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13092 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13093 player->programmed_action = MV_DOWN;
13098 MovePlayerOneStep()
13099 -----------------------------------------------------------------------------
13100 dx, dy: direction (non-diagonal) to try to move the player to
13101 real_dx, real_dy: direction as read from input device (can be diagonal)
13104 boolean MovePlayerOneStep(struct PlayerInfo *player,
13105 int dx, int dy, int real_dx, int real_dy)
13107 int jx = player->jx, jy = player->jy;
13108 int new_jx = jx + dx, new_jy = jy + dy;
13109 #if !USE_FIXED_DONT_RUN_INTO
13113 boolean player_can_move = !player->cannot_move;
13115 if (!player->active || (!dx && !dy))
13116 return MP_NO_ACTION;
13118 player->MovDir = (dx < 0 ? MV_LEFT :
13119 dx > 0 ? MV_RIGHT :
13121 dy > 0 ? MV_DOWN : MV_NONE);
13123 if (!IN_LEV_FIELD(new_jx, new_jy))
13124 return MP_NO_ACTION;
13126 if (!player_can_move)
13128 if (player->MovPos == 0)
13130 player->is_moving = FALSE;
13131 player->is_digging = FALSE;
13132 player->is_collecting = FALSE;
13133 player->is_snapping = FALSE;
13134 player->is_pushing = FALSE;
13139 if (!options.network && game.centered_player_nr == -1 &&
13140 !AllPlayersInSight(player, new_jx, new_jy))
13141 return MP_NO_ACTION;
13143 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13144 return MP_NO_ACTION;
13147 #if !USE_FIXED_DONT_RUN_INTO
13148 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13150 /* (moved to DigField()) */
13151 if (player_can_move && DONT_RUN_INTO(element))
13153 if (element == EL_ACID && dx == 0 && dy == 1)
13155 SplashAcid(new_jx, new_jy);
13156 Feld[jx][jy] = EL_PLAYER_1;
13157 InitMovingField(jx, jy, MV_DOWN);
13158 Store[jx][jy] = EL_ACID;
13159 ContinueMoving(jx, jy);
13160 BuryPlayer(player);
13163 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13169 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13170 if (can_move != MP_MOVING)
13173 /* check if DigField() has caused relocation of the player */
13174 if (player->jx != jx || player->jy != jy)
13175 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13177 StorePlayer[jx][jy] = 0;
13178 player->last_jx = jx;
13179 player->last_jy = jy;
13180 player->jx = new_jx;
13181 player->jy = new_jy;
13182 StorePlayer[new_jx][new_jy] = player->element_nr;
13184 if (player->move_delay_value_next != -1)
13186 player->move_delay_value = player->move_delay_value_next;
13187 player->move_delay_value_next = -1;
13191 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13193 player->step_counter++;
13195 PlayerVisit[jx][jy] = FrameCounter;
13197 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13198 player->is_moving = TRUE;
13202 /* should better be called in MovePlayer(), but this breaks some tapes */
13203 ScrollPlayer(player, SCROLL_INIT);
13209 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13211 int jx = player->jx, jy = player->jy;
13212 int old_jx = jx, old_jy = jy;
13213 int moved = MP_NO_ACTION;
13215 if (!player->active)
13220 if (player->MovPos == 0)
13222 player->is_moving = FALSE;
13223 player->is_digging = FALSE;
13224 player->is_collecting = FALSE;
13225 player->is_snapping = FALSE;
13226 player->is_pushing = FALSE;
13232 if (player->move_delay > 0)
13235 player->move_delay = -1; /* set to "uninitialized" value */
13237 /* store if player is automatically moved to next field */
13238 player->is_auto_moving = (player->programmed_action != MV_NONE);
13240 /* remove the last programmed player action */
13241 player->programmed_action = 0;
13243 if (player->MovPos)
13245 /* should only happen if pre-1.2 tape recordings are played */
13246 /* this is only for backward compatibility */
13248 int original_move_delay_value = player->move_delay_value;
13251 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13255 /* scroll remaining steps with finest movement resolution */
13256 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13258 while (player->MovPos)
13260 ScrollPlayer(player, SCROLL_GO_ON);
13261 ScrollScreen(NULL, SCROLL_GO_ON);
13263 AdvanceFrameAndPlayerCounters(player->index_nr);
13269 player->move_delay_value = original_move_delay_value;
13272 player->is_active = FALSE;
13274 if (player->last_move_dir & MV_HORIZONTAL)
13276 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13277 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13281 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13282 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13285 #if USE_FIXED_BORDER_RUNNING_GFX
13286 if (!moved && !player->is_active)
13288 player->is_moving = FALSE;
13289 player->is_digging = FALSE;
13290 player->is_collecting = FALSE;
13291 player->is_snapping = FALSE;
13292 player->is_pushing = FALSE;
13300 if (moved & MP_MOVING && !ScreenMovPos &&
13301 (player->index_nr == game.centered_player_nr ||
13302 game.centered_player_nr == -1))
13304 if (moved & MP_MOVING && !ScreenMovPos &&
13305 (player == local_player || !options.network))
13308 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13309 int offset = game.scroll_delay_value;
13311 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13313 /* actual player has left the screen -- scroll in that direction */
13314 if (jx != old_jx) /* player has moved horizontally */
13315 scroll_x += (jx - old_jx);
13316 else /* player has moved vertically */
13317 scroll_y += (jy - old_jy);
13321 if (jx != old_jx) /* player has moved horizontally */
13323 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13324 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13325 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13327 /* don't scroll over playfield boundaries */
13328 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13329 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13331 /* don't scroll more than one field at a time */
13332 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13334 /* don't scroll against the player's moving direction */
13335 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13336 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13337 scroll_x = old_scroll_x;
13339 else /* player has moved vertically */
13341 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13342 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13343 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13345 /* don't scroll over playfield boundaries */
13346 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13347 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13349 /* don't scroll more than one field at a time */
13350 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13352 /* don't scroll against the player's moving direction */
13353 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13354 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13355 scroll_y = old_scroll_y;
13359 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13362 if (!options.network && game.centered_player_nr == -1 &&
13363 !AllPlayersInVisibleScreen())
13365 scroll_x = old_scroll_x;
13366 scroll_y = old_scroll_y;
13370 if (!options.network && !AllPlayersInVisibleScreen())
13372 scroll_x = old_scroll_x;
13373 scroll_y = old_scroll_y;
13378 ScrollScreen(player, SCROLL_INIT);
13379 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13384 player->StepFrame = 0;
13386 if (moved & MP_MOVING)
13388 if (old_jx != jx && old_jy == jy)
13389 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13390 else if (old_jx == jx && old_jy != jy)
13391 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13393 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13395 player->last_move_dir = player->MovDir;
13396 player->is_moving = TRUE;
13397 player->is_snapping = FALSE;
13398 player->is_switching = FALSE;
13399 player->is_dropping = FALSE;
13400 player->is_dropping_pressed = FALSE;
13401 player->drop_pressed_delay = 0;
13404 /* should better be called here than above, but this breaks some tapes */
13405 ScrollPlayer(player, SCROLL_INIT);
13410 CheckGravityMovementWhenNotMoving(player);
13412 player->is_moving = FALSE;
13414 /* at this point, the player is allowed to move, but cannot move right now
13415 (e.g. because of something blocking the way) -- ensure that the player
13416 is also allowed to move in the next frame (in old versions before 3.1.1,
13417 the player was forced to wait again for eight frames before next try) */
13419 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13420 player->move_delay = 0; /* allow direct movement in the next frame */
13423 if (player->move_delay == -1) /* not yet initialized by DigField() */
13424 player->move_delay = player->move_delay_value;
13426 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13428 TestIfPlayerTouchesBadThing(jx, jy);
13429 TestIfPlayerTouchesCustomElement(jx, jy);
13432 if (!player->active)
13433 RemovePlayer(player);
13438 void ScrollPlayer(struct PlayerInfo *player, int mode)
13440 int jx = player->jx, jy = player->jy;
13441 int last_jx = player->last_jx, last_jy = player->last_jy;
13442 int move_stepsize = TILEX / player->move_delay_value;
13444 #if USE_NEW_PLAYER_SPEED
13445 if (!player->active)
13448 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13451 if (!player->active || player->MovPos == 0)
13455 if (mode == SCROLL_INIT)
13457 player->actual_frame_counter = FrameCounter;
13458 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13460 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13461 Feld[last_jx][last_jy] == EL_EMPTY)
13463 int last_field_block_delay = 0; /* start with no blocking at all */
13464 int block_delay_adjustment = player->block_delay_adjustment;
13466 /* if player blocks last field, add delay for exactly one move */
13467 if (player->block_last_field)
13469 last_field_block_delay += player->move_delay_value;
13471 /* when blocking enabled, prevent moving up despite gravity */
13472 #if USE_PLAYER_GRAVITY
13473 if (player->gravity && player->MovDir == MV_UP)
13474 block_delay_adjustment = -1;
13476 if (game.gravity && player->MovDir == MV_UP)
13477 block_delay_adjustment = -1;
13481 /* add block delay adjustment (also possible when not blocking) */
13482 last_field_block_delay += block_delay_adjustment;
13484 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13485 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13488 #if USE_NEW_PLAYER_SPEED
13489 if (player->MovPos != 0) /* player has not yet reached destination */
13495 else if (!FrameReached(&player->actual_frame_counter, 1))
13498 #if USE_NEW_PLAYER_SPEED
13499 if (player->MovPos != 0)
13501 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13502 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13504 /* before DrawPlayer() to draw correct player graphic for this case */
13505 if (player->MovPos == 0)
13506 CheckGravityMovement(player);
13509 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13510 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13512 /* before DrawPlayer() to draw correct player graphic for this case */
13513 if (player->MovPos == 0)
13514 CheckGravityMovement(player);
13517 if (player->MovPos == 0) /* player reached destination field */
13519 if (player->move_delay_reset_counter > 0)
13521 player->move_delay_reset_counter--;
13523 if (player->move_delay_reset_counter == 0)
13525 /* continue with normal speed after quickly moving through gate */
13526 HALVE_PLAYER_SPEED(player);
13528 /* be able to make the next move without delay */
13529 player->move_delay = 0;
13533 player->last_jx = jx;
13534 player->last_jy = jy;
13536 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13537 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13538 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13539 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13540 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13541 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13543 DrawPlayer(player); /* needed here only to cleanup last field */
13544 RemovePlayer(player);
13546 if (local_player->friends_still_needed == 0 ||
13547 IS_SP_ELEMENT(Feld[jx][jy]))
13548 PlayerWins(player);
13551 /* this breaks one level: "machine", level 000 */
13553 int move_direction = player->MovDir;
13554 int enter_side = MV_DIR_OPPOSITE(move_direction);
13555 int leave_side = move_direction;
13556 int old_jx = last_jx;
13557 int old_jy = last_jy;
13558 int old_element = Feld[old_jx][old_jy];
13559 int new_element = Feld[jx][jy];
13561 if (IS_CUSTOM_ELEMENT(old_element))
13562 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13564 player->index_bit, leave_side);
13566 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13567 CE_PLAYER_LEAVES_X,
13568 player->index_bit, leave_side);
13570 if (IS_CUSTOM_ELEMENT(new_element))
13571 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13572 player->index_bit, enter_side);
13574 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13575 CE_PLAYER_ENTERS_X,
13576 player->index_bit, enter_side);
13578 #if USE_FIX_CE_ACTION_WITH_PLAYER
13579 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13580 CE_MOVE_OF_X, move_direction);
13582 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13583 CE_MOVE_OF_X, move_direction);
13587 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13589 TestIfPlayerTouchesBadThing(jx, jy);
13590 TestIfPlayerTouchesCustomElement(jx, jy);
13592 /* needed because pushed element has not yet reached its destination,
13593 so it would trigger a change event at its previous field location */
13594 if (!player->is_pushing)
13595 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13597 if (!player->active)
13598 RemovePlayer(player);
13601 if (!local_player->LevelSolved && level.use_step_counter)
13611 if (TimeLeft <= 10 && setup.time_limit)
13612 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13615 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13617 DisplayGameControlValues();
13619 DrawGameValue_Time(TimeLeft);
13622 if (!TimeLeft && setup.time_limit)
13623 for (i = 0; i < MAX_PLAYERS; i++)
13624 KillPlayer(&stored_player[i]);
13627 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13629 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13631 DisplayGameControlValues();
13634 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13635 DrawGameValue_Time(TimePlayed);
13639 if (tape.single_step && tape.recording && !tape.pausing &&
13640 !player->programmed_action)
13641 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13645 void ScrollScreen(struct PlayerInfo *player, int mode)
13647 static unsigned long screen_frame_counter = 0;
13649 if (mode == SCROLL_INIT)
13651 /* set scrolling step size according to actual player's moving speed */
13652 ScrollStepSize = TILEX / player->move_delay_value;
13654 screen_frame_counter = FrameCounter;
13655 ScreenMovDir = player->MovDir;
13656 ScreenMovPos = player->MovPos;
13657 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13660 else if (!FrameReached(&screen_frame_counter, 1))
13665 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13666 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13667 redraw_mask |= REDRAW_FIELD;
13670 ScreenMovDir = MV_NONE;
13673 void TestIfPlayerTouchesCustomElement(int x, int y)
13675 static int xy[4][2] =
13682 static int trigger_sides[4][2] =
13684 /* center side border side */
13685 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13686 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13687 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13688 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13690 static int touch_dir[4] =
13692 MV_LEFT | MV_RIGHT,
13697 int center_element = Feld[x][y]; /* should always be non-moving! */
13700 for (i = 0; i < NUM_DIRECTIONS; i++)
13702 int xx = x + xy[i][0];
13703 int yy = y + xy[i][1];
13704 int center_side = trigger_sides[i][0];
13705 int border_side = trigger_sides[i][1];
13706 int border_element;
13708 if (!IN_LEV_FIELD(xx, yy))
13711 if (IS_PLAYER(x, y)) /* player found at center element */
13713 struct PlayerInfo *player = PLAYERINFO(x, y);
13715 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13716 border_element = Feld[xx][yy]; /* may be moving! */
13717 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13718 border_element = Feld[xx][yy];
13719 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13720 border_element = MovingOrBlocked2Element(xx, yy);
13722 continue; /* center and border element do not touch */
13724 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13725 player->index_bit, border_side);
13726 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13727 CE_PLAYER_TOUCHES_X,
13728 player->index_bit, border_side);
13730 #if USE_FIX_CE_ACTION_WITH_PLAYER
13732 /* use player element that is initially defined in the level playfield,
13733 not the player element that corresponds to the runtime player number
13734 (example: a level that contains EL_PLAYER_3 as the only player would
13735 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13736 int player_element = PLAYERINFO(x, y)->initial_element;
13738 CheckElementChangeBySide(xx, yy, border_element, player_element,
13739 CE_TOUCHING_X, border_side);
13743 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13745 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13747 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13749 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13750 continue; /* center and border element do not touch */
13753 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13754 player->index_bit, center_side);
13755 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13756 CE_PLAYER_TOUCHES_X,
13757 player->index_bit, center_side);
13759 #if USE_FIX_CE_ACTION_WITH_PLAYER
13761 /* use player element that is initially defined in the level playfield,
13762 not the player element that corresponds to the runtime player number
13763 (example: a level that contains EL_PLAYER_3 as the only player would
13764 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13765 int player_element = PLAYERINFO(xx, yy)->initial_element;
13767 CheckElementChangeBySide(x, y, center_element, player_element,
13768 CE_TOUCHING_X, center_side);
13777 #if USE_ELEMENT_TOUCHING_BUGFIX
13779 void TestIfElementTouchesCustomElement(int x, int y)
13781 static int xy[4][2] =
13788 static int trigger_sides[4][2] =
13790 /* center side border side */
13791 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13792 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13793 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13794 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13796 static int touch_dir[4] =
13798 MV_LEFT | MV_RIGHT,
13803 boolean change_center_element = FALSE;
13804 int center_element = Feld[x][y]; /* should always be non-moving! */
13805 int border_element_old[NUM_DIRECTIONS];
13808 for (i = 0; i < NUM_DIRECTIONS; i++)
13810 int xx = x + xy[i][0];
13811 int yy = y + xy[i][1];
13812 int border_element;
13814 border_element_old[i] = -1;
13816 if (!IN_LEV_FIELD(xx, yy))
13819 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13820 border_element = Feld[xx][yy]; /* may be moving! */
13821 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13822 border_element = Feld[xx][yy];
13823 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13824 border_element = MovingOrBlocked2Element(xx, yy);
13826 continue; /* center and border element do not touch */
13828 border_element_old[i] = border_element;
13831 for (i = 0; i < NUM_DIRECTIONS; i++)
13833 int xx = x + xy[i][0];
13834 int yy = y + xy[i][1];
13835 int center_side = trigger_sides[i][0];
13836 int border_element = border_element_old[i];
13838 if (border_element == -1)
13841 /* check for change of border element */
13842 CheckElementChangeBySide(xx, yy, border_element, center_element,
13843 CE_TOUCHING_X, center_side);
13845 /* (center element cannot be player, so we dont have to check this here) */
13848 for (i = 0; i < NUM_DIRECTIONS; i++)
13850 int xx = x + xy[i][0];
13851 int yy = y + xy[i][1];
13852 int border_side = trigger_sides[i][1];
13853 int border_element = border_element_old[i];
13855 if (border_element == -1)
13858 /* check for change of center element (but change it only once) */
13859 if (!change_center_element)
13860 change_center_element =
13861 CheckElementChangeBySide(x, y, center_element, border_element,
13862 CE_TOUCHING_X, border_side);
13864 #if USE_FIX_CE_ACTION_WITH_PLAYER
13865 if (IS_PLAYER(xx, yy))
13867 /* use player element that is initially defined in the level playfield,
13868 not the player element that corresponds to the runtime player number
13869 (example: a level that contains EL_PLAYER_3 as the only player would
13870 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13871 int player_element = PLAYERINFO(xx, yy)->initial_element;
13873 CheckElementChangeBySide(x, y, center_element, player_element,
13874 CE_TOUCHING_X, border_side);
13882 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13884 static int xy[4][2] =
13891 static int trigger_sides[4][2] =
13893 /* center side border side */
13894 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13895 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13896 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13897 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13899 static int touch_dir[4] =
13901 MV_LEFT | MV_RIGHT,
13906 boolean change_center_element = FALSE;
13907 int center_element = Feld[x][y]; /* should always be non-moving! */
13910 for (i = 0; i < NUM_DIRECTIONS; i++)
13912 int xx = x + xy[i][0];
13913 int yy = y + xy[i][1];
13914 int center_side = trigger_sides[i][0];
13915 int border_side = trigger_sides[i][1];
13916 int border_element;
13918 if (!IN_LEV_FIELD(xx, yy))
13921 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13922 border_element = Feld[xx][yy]; /* may be moving! */
13923 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13924 border_element = Feld[xx][yy];
13925 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13926 border_element = MovingOrBlocked2Element(xx, yy);
13928 continue; /* center and border element do not touch */
13930 /* check for change of center element (but change it only once) */
13931 if (!change_center_element)
13932 change_center_element =
13933 CheckElementChangeBySide(x, y, center_element, border_element,
13934 CE_TOUCHING_X, border_side);
13936 /* check for change of border element */
13937 CheckElementChangeBySide(xx, yy, border_element, center_element,
13938 CE_TOUCHING_X, center_side);
13944 void TestIfElementHitsCustomElement(int x, int y, int direction)
13946 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13947 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13948 int hitx = x + dx, hity = y + dy;
13949 int hitting_element = Feld[x][y];
13950 int touched_element;
13952 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13955 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13956 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13958 if (IN_LEV_FIELD(hitx, hity))
13960 int opposite_direction = MV_DIR_OPPOSITE(direction);
13961 int hitting_side = direction;
13962 int touched_side = opposite_direction;
13963 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13964 MovDir[hitx][hity] != direction ||
13965 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13971 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13972 CE_HITTING_X, touched_side);
13974 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13975 CE_HIT_BY_X, hitting_side);
13977 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13978 CE_HIT_BY_SOMETHING, opposite_direction);
13980 #if USE_FIX_CE_ACTION_WITH_PLAYER
13981 if (IS_PLAYER(hitx, hity))
13983 /* use player element that is initially defined in the level playfield,
13984 not the player element that corresponds to the runtime player number
13985 (example: a level that contains EL_PLAYER_3 as the only player would
13986 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13987 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13989 CheckElementChangeBySide(x, y, hitting_element, player_element,
13990 CE_HITTING_X, touched_side);
13996 /* "hitting something" is also true when hitting the playfield border */
13997 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13998 CE_HITTING_SOMETHING, direction);
14002 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14004 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14005 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14006 int hitx = x + dx, hity = y + dy;
14007 int hitting_element = Feld[x][y];
14008 int touched_element;
14010 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14011 !IS_FREE(hitx, hity) &&
14012 (!IS_MOVING(hitx, hity) ||
14013 MovDir[hitx][hity] != direction ||
14014 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14017 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14021 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14025 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14026 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14028 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14029 EP_CAN_SMASH_EVERYTHING, direction);
14031 if (IN_LEV_FIELD(hitx, hity))
14033 int opposite_direction = MV_DIR_OPPOSITE(direction);
14034 int hitting_side = direction;
14035 int touched_side = opposite_direction;
14037 int touched_element = MovingOrBlocked2Element(hitx, hity);
14040 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14041 MovDir[hitx][hity] != direction ||
14042 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14051 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14052 CE_SMASHED_BY_SOMETHING, opposite_direction);
14054 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14055 CE_OTHER_IS_SMASHING, touched_side);
14057 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14058 CE_OTHER_GETS_SMASHED, hitting_side);
14064 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14066 int i, kill_x = -1, kill_y = -1;
14068 int bad_element = -1;
14069 static int test_xy[4][2] =
14076 static int test_dir[4] =
14084 for (i = 0; i < NUM_DIRECTIONS; i++)
14086 int test_x, test_y, test_move_dir, test_element;
14088 test_x = good_x + test_xy[i][0];
14089 test_y = good_y + test_xy[i][1];
14091 if (!IN_LEV_FIELD(test_x, test_y))
14095 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14097 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14099 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14100 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14102 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14103 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14107 bad_element = test_element;
14113 if (kill_x != -1 || kill_y != -1)
14115 if (IS_PLAYER(good_x, good_y))
14117 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14119 if (player->shield_deadly_time_left > 0 &&
14120 !IS_INDESTRUCTIBLE(bad_element))
14121 Bang(kill_x, kill_y);
14122 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14123 KillPlayer(player);
14126 Bang(good_x, good_y);
14130 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14132 int i, kill_x = -1, kill_y = -1;
14133 int bad_element = Feld[bad_x][bad_y];
14134 static int test_xy[4][2] =
14141 static int touch_dir[4] =
14143 MV_LEFT | MV_RIGHT,
14148 static int test_dir[4] =
14156 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14159 for (i = 0; i < NUM_DIRECTIONS; i++)
14161 int test_x, test_y, test_move_dir, test_element;
14163 test_x = bad_x + test_xy[i][0];
14164 test_y = bad_y + test_xy[i][1];
14166 if (!IN_LEV_FIELD(test_x, test_y))
14170 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14172 test_element = Feld[test_x][test_y];
14174 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14175 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14177 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14178 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14180 /* good thing is player or penguin that does not move away */
14181 if (IS_PLAYER(test_x, test_y))
14183 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14185 if (bad_element == EL_ROBOT && player->is_moving)
14186 continue; /* robot does not kill player if he is moving */
14188 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14190 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14191 continue; /* center and border element do not touch */
14199 else if (test_element == EL_PENGUIN)
14209 if (kill_x != -1 || kill_y != -1)
14211 if (IS_PLAYER(kill_x, kill_y))
14213 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14215 if (player->shield_deadly_time_left > 0 &&
14216 !IS_INDESTRUCTIBLE(bad_element))
14217 Bang(bad_x, bad_y);
14218 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14219 KillPlayer(player);
14222 Bang(kill_x, kill_y);
14226 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14228 int bad_element = Feld[bad_x][bad_y];
14229 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14230 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14231 int test_x = bad_x + dx, test_y = bad_y + dy;
14232 int test_move_dir, test_element;
14233 int kill_x = -1, kill_y = -1;
14235 if (!IN_LEV_FIELD(test_x, test_y))
14239 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14241 test_element = Feld[test_x][test_y];
14243 if (test_move_dir != bad_move_dir)
14245 /* good thing can be player or penguin that does not move away */
14246 if (IS_PLAYER(test_x, test_y))
14248 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14250 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14251 player as being hit when he is moving towards the bad thing, because
14252 the "get hit by" condition would be lost after the player stops) */
14253 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14254 return; /* player moves away from bad thing */
14259 else if (test_element == EL_PENGUIN)
14266 if (kill_x != -1 || kill_y != -1)
14268 if (IS_PLAYER(kill_x, kill_y))
14270 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14272 if (player->shield_deadly_time_left > 0 &&
14273 !IS_INDESTRUCTIBLE(bad_element))
14274 Bang(bad_x, bad_y);
14275 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14276 KillPlayer(player);
14279 Bang(kill_x, kill_y);
14283 void TestIfPlayerTouchesBadThing(int x, int y)
14285 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14288 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14290 TestIfGoodThingHitsBadThing(x, y, move_dir);
14293 void TestIfBadThingTouchesPlayer(int x, int y)
14295 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14298 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14300 TestIfBadThingHitsGoodThing(x, y, move_dir);
14303 void TestIfFriendTouchesBadThing(int x, int y)
14305 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14308 void TestIfBadThingTouchesFriend(int x, int y)
14310 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14313 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14315 int i, kill_x = bad_x, kill_y = bad_y;
14316 static int xy[4][2] =
14324 for (i = 0; i < NUM_DIRECTIONS; i++)
14328 x = bad_x + xy[i][0];
14329 y = bad_y + xy[i][1];
14330 if (!IN_LEV_FIELD(x, y))
14333 element = Feld[x][y];
14334 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14335 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14343 if (kill_x != bad_x || kill_y != bad_y)
14344 Bang(bad_x, bad_y);
14347 void KillPlayer(struct PlayerInfo *player)
14349 int jx = player->jx, jy = player->jy;
14351 if (!player->active)
14355 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14356 player->killed, player->active, player->reanimated);
14359 /* the following code was introduced to prevent an infinite loop when calling
14361 -> CheckTriggeredElementChangeExt()
14362 -> ExecuteCustomElementAction()
14364 -> (infinitely repeating the above sequence of function calls)
14365 which occurs when killing the player while having a CE with the setting
14366 "kill player X when explosion of <player X>"; the solution using a new
14367 field "player->killed" was chosen for backwards compatibility, although
14368 clever use of the fields "player->active" etc. would probably also work */
14370 if (player->killed)
14374 player->killed = TRUE;
14376 /* remove accessible field at the player's position */
14377 Feld[jx][jy] = EL_EMPTY;
14379 /* deactivate shield (else Bang()/Explode() would not work right) */
14380 player->shield_normal_time_left = 0;
14381 player->shield_deadly_time_left = 0;
14384 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14385 player->killed, player->active, player->reanimated);
14391 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14392 player->killed, player->active, player->reanimated);
14395 #if USE_PLAYER_REANIMATION
14397 if (player->reanimated) /* killed player may have been reanimated */
14398 player->killed = player->reanimated = FALSE;
14400 BuryPlayer(player);
14402 if (player->killed) /* player may have been reanimated */
14403 BuryPlayer(player);
14406 BuryPlayer(player);
14410 static void KillPlayerUnlessEnemyProtected(int x, int y)
14412 if (!PLAYER_ENEMY_PROTECTED(x, y))
14413 KillPlayer(PLAYERINFO(x, y));
14416 static void KillPlayerUnlessExplosionProtected(int x, int y)
14418 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14419 KillPlayer(PLAYERINFO(x, y));
14422 void BuryPlayer(struct PlayerInfo *player)
14424 int jx = player->jx, jy = player->jy;
14426 if (!player->active)
14429 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14430 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14432 player->GameOver = TRUE;
14433 RemovePlayer(player);
14436 void RemovePlayer(struct PlayerInfo *player)
14438 int jx = player->jx, jy = player->jy;
14439 int i, found = FALSE;
14441 player->present = FALSE;
14442 player->active = FALSE;
14444 if (!ExplodeField[jx][jy])
14445 StorePlayer[jx][jy] = 0;
14447 if (player->is_moving)
14448 TEST_DrawLevelField(player->last_jx, player->last_jy);
14450 for (i = 0; i < MAX_PLAYERS; i++)
14451 if (stored_player[i].active)
14455 AllPlayersGone = TRUE;
14461 #if USE_NEW_SNAP_DELAY
14462 static void setFieldForSnapping(int x, int y, int element, int direction)
14464 struct ElementInfo *ei = &element_info[element];
14465 int direction_bit = MV_DIR_TO_BIT(direction);
14466 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14467 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14468 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14470 Feld[x][y] = EL_ELEMENT_SNAPPING;
14471 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14473 ResetGfxAnimation(x, y);
14475 GfxElement[x][y] = element;
14476 GfxAction[x][y] = action;
14477 GfxDir[x][y] = direction;
14478 GfxFrame[x][y] = -1;
14483 =============================================================================
14484 checkDiagonalPushing()
14485 -----------------------------------------------------------------------------
14486 check if diagonal input device direction results in pushing of object
14487 (by checking if the alternative direction is walkable, diggable, ...)
14488 =============================================================================
14491 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14492 int x, int y, int real_dx, int real_dy)
14494 int jx, jy, dx, dy, xx, yy;
14496 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14499 /* diagonal direction: check alternative direction */
14504 xx = jx + (dx == 0 ? real_dx : 0);
14505 yy = jy + (dy == 0 ? real_dy : 0);
14507 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14511 =============================================================================
14513 -----------------------------------------------------------------------------
14514 x, y: field next to player (non-diagonal) to try to dig to
14515 real_dx, real_dy: direction as read from input device (can be diagonal)
14516 =============================================================================
14519 static int DigField(struct PlayerInfo *player,
14520 int oldx, int oldy, int x, int y,
14521 int real_dx, int real_dy, int mode)
14523 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14524 boolean player_was_pushing = player->is_pushing;
14525 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14526 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14527 int jx = oldx, jy = oldy;
14528 int dx = x - jx, dy = y - jy;
14529 int nextx = x + dx, nexty = y + dy;
14530 int move_direction = (dx == -1 ? MV_LEFT :
14531 dx == +1 ? MV_RIGHT :
14533 dy == +1 ? MV_DOWN : MV_NONE);
14534 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14535 int dig_side = MV_DIR_OPPOSITE(move_direction);
14536 int old_element = Feld[jx][jy];
14537 #if USE_FIXED_DONT_RUN_INTO
14538 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14544 if (is_player) /* function can also be called by EL_PENGUIN */
14546 if (player->MovPos == 0)
14548 player->is_digging = FALSE;
14549 player->is_collecting = FALSE;
14552 if (player->MovPos == 0) /* last pushing move finished */
14553 player->is_pushing = FALSE;
14555 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14557 player->is_switching = FALSE;
14558 player->push_delay = -1;
14560 return MP_NO_ACTION;
14564 #if !USE_FIXED_DONT_RUN_INTO
14565 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14566 return MP_NO_ACTION;
14569 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14570 old_element = Back[jx][jy];
14572 /* in case of element dropped at player position, check background */
14573 else if (Back[jx][jy] != EL_EMPTY &&
14574 game.engine_version >= VERSION_IDENT(2,2,0,0))
14575 old_element = Back[jx][jy];
14577 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14578 return MP_NO_ACTION; /* field has no opening in this direction */
14580 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14581 return MP_NO_ACTION; /* field has no opening in this direction */
14583 #if USE_FIXED_DONT_RUN_INTO
14584 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14588 Feld[jx][jy] = player->artwork_element;
14589 InitMovingField(jx, jy, MV_DOWN);
14590 Store[jx][jy] = EL_ACID;
14591 ContinueMoving(jx, jy);
14592 BuryPlayer(player);
14594 return MP_DONT_RUN_INTO;
14598 #if USE_FIXED_DONT_RUN_INTO
14599 if (player_can_move && DONT_RUN_INTO(element))
14601 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14603 return MP_DONT_RUN_INTO;
14607 #if USE_FIXED_DONT_RUN_INTO
14608 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14609 return MP_NO_ACTION;
14612 #if !USE_FIXED_DONT_RUN_INTO
14613 element = Feld[x][y];
14616 collect_count = element_info[element].collect_count_initial;
14618 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14619 return MP_NO_ACTION;
14621 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14622 player_can_move = player_can_move_or_snap;
14624 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14625 game.engine_version >= VERSION_IDENT(2,2,0,0))
14627 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14628 player->index_bit, dig_side);
14629 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14630 player->index_bit, dig_side);
14632 if (element == EL_DC_LANDMINE)
14635 if (Feld[x][y] != element) /* field changed by snapping */
14638 return MP_NO_ACTION;
14641 #if USE_PLAYER_GRAVITY
14642 if (player->gravity && is_player && !player->is_auto_moving &&
14643 canFallDown(player) && move_direction != MV_DOWN &&
14644 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14645 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14647 if (game.gravity && is_player && !player->is_auto_moving &&
14648 canFallDown(player) && move_direction != MV_DOWN &&
14649 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14650 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14653 if (player_can_move &&
14654 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14656 int sound_element = SND_ELEMENT(element);
14657 int sound_action = ACTION_WALKING;
14659 if (IS_RND_GATE(element))
14661 if (!player->key[RND_GATE_NR(element)])
14662 return MP_NO_ACTION;
14664 else if (IS_RND_GATE_GRAY(element))
14666 if (!player->key[RND_GATE_GRAY_NR(element)])
14667 return MP_NO_ACTION;
14669 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14671 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14672 return MP_NO_ACTION;
14674 else if (element == EL_EXIT_OPEN ||
14675 element == EL_EM_EXIT_OPEN ||
14676 element == EL_STEEL_EXIT_OPEN ||
14677 element == EL_EM_STEEL_EXIT_OPEN ||
14678 element == EL_SP_EXIT_OPEN ||
14679 element == EL_SP_EXIT_OPENING)
14681 sound_action = ACTION_PASSING; /* player is passing exit */
14683 else if (element == EL_EMPTY)
14685 sound_action = ACTION_MOVING; /* nothing to walk on */
14688 /* play sound from background or player, whatever is available */
14689 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14690 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14692 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14694 else if (player_can_move &&
14695 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14697 if (!ACCESS_FROM(element, opposite_direction))
14698 return MP_NO_ACTION; /* field not accessible from this direction */
14700 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14701 return MP_NO_ACTION;
14703 if (IS_EM_GATE(element))
14705 if (!player->key[EM_GATE_NR(element)])
14706 return MP_NO_ACTION;
14708 else if (IS_EM_GATE_GRAY(element))
14710 if (!player->key[EM_GATE_GRAY_NR(element)])
14711 return MP_NO_ACTION;
14713 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14715 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14716 return MP_NO_ACTION;
14718 else if (IS_EMC_GATE(element))
14720 if (!player->key[EMC_GATE_NR(element)])
14721 return MP_NO_ACTION;
14723 else if (IS_EMC_GATE_GRAY(element))
14725 if (!player->key[EMC_GATE_GRAY_NR(element)])
14726 return MP_NO_ACTION;
14728 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14730 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14731 return MP_NO_ACTION;
14733 else if (element == EL_DC_GATE_WHITE ||
14734 element == EL_DC_GATE_WHITE_GRAY ||
14735 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14737 if (player->num_white_keys == 0)
14738 return MP_NO_ACTION;
14740 player->num_white_keys--;
14742 else if (IS_SP_PORT(element))
14744 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14745 element == EL_SP_GRAVITY_PORT_RIGHT ||
14746 element == EL_SP_GRAVITY_PORT_UP ||
14747 element == EL_SP_GRAVITY_PORT_DOWN)
14748 #if USE_PLAYER_GRAVITY
14749 player->gravity = !player->gravity;
14751 game.gravity = !game.gravity;
14753 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14754 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14755 element == EL_SP_GRAVITY_ON_PORT_UP ||
14756 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14757 #if USE_PLAYER_GRAVITY
14758 player->gravity = TRUE;
14760 game.gravity = TRUE;
14762 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14763 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14764 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14765 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14766 #if USE_PLAYER_GRAVITY
14767 player->gravity = FALSE;
14769 game.gravity = FALSE;
14773 /* automatically move to the next field with double speed */
14774 player->programmed_action = move_direction;
14776 if (player->move_delay_reset_counter == 0)
14778 player->move_delay_reset_counter = 2; /* two double speed steps */
14780 DOUBLE_PLAYER_SPEED(player);
14783 PlayLevelSoundAction(x, y, ACTION_PASSING);
14785 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14789 if (mode != DF_SNAP)
14791 GfxElement[x][y] = GFX_ELEMENT(element);
14792 player->is_digging = TRUE;
14795 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14797 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_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_COLLECTIBLE(element))
14819 if (is_player && mode != DF_SNAP)
14821 GfxElement[x][y] = element;
14822 player->is_collecting = TRUE;
14825 if (element == EL_SPEED_PILL)
14827 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14829 else if (element == EL_EXTRA_TIME && level.time > 0)
14831 TimeLeft += level.extra_time;
14834 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14836 DisplayGameControlValues();
14838 DrawGameValue_Time(TimeLeft);
14841 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14843 player->shield_normal_time_left += level.shield_normal_time;
14844 if (element == EL_SHIELD_DEADLY)
14845 player->shield_deadly_time_left += level.shield_deadly_time;
14847 else if (element == EL_DYNAMITE ||
14848 element == EL_EM_DYNAMITE ||
14849 element == EL_SP_DISK_RED)
14851 if (player->inventory_size < MAX_INVENTORY_SIZE)
14852 player->inventory_element[player->inventory_size++] = element;
14854 DrawGameDoorValues();
14856 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14858 player->dynabomb_count++;
14859 player->dynabombs_left++;
14861 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14863 player->dynabomb_size++;
14865 else if (element == EL_DYNABOMB_INCREASE_POWER)
14867 player->dynabomb_xl = TRUE;
14869 else if (IS_KEY(element))
14871 player->key[KEY_NR(element)] = TRUE;
14873 DrawGameDoorValues();
14875 else if (element == EL_DC_KEY_WHITE)
14877 player->num_white_keys++;
14879 /* display white keys? */
14880 /* DrawGameDoorValues(); */
14882 else if (IS_ENVELOPE(element))
14884 player->show_envelope = element;
14886 else if (element == EL_EMC_LENSES)
14888 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14890 RedrawAllInvisibleElementsForLenses();
14892 else if (element == EL_EMC_MAGNIFIER)
14894 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14896 RedrawAllInvisibleElementsForMagnifier();
14898 else if (IS_DROPPABLE(element) ||
14899 IS_THROWABLE(element)) /* can be collected and dropped */
14903 if (collect_count == 0)
14904 player->inventory_infinite_element = element;
14906 for (i = 0; i < collect_count; i++)
14907 if (player->inventory_size < MAX_INVENTORY_SIZE)
14908 player->inventory_element[player->inventory_size++] = element;
14910 DrawGameDoorValues();
14912 else if (collect_count > 0)
14914 local_player->gems_still_needed -= collect_count;
14915 if (local_player->gems_still_needed < 0)
14916 local_player->gems_still_needed = 0;
14919 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14921 DisplayGameControlValues();
14923 DrawGameValue_Emeralds(local_player->gems_still_needed);
14927 RaiseScoreElement(element);
14928 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14931 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14932 player->index_bit, dig_side);
14934 if (mode == DF_SNAP)
14936 #if USE_NEW_SNAP_DELAY
14937 if (level.block_snap_field)
14938 setFieldForSnapping(x, y, element, move_direction);
14940 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14942 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14945 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14946 player->index_bit, dig_side);
14949 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14951 if (mode == DF_SNAP && element != EL_BD_ROCK)
14952 return MP_NO_ACTION;
14954 if (CAN_FALL(element) && dy)
14955 return MP_NO_ACTION;
14957 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14958 !(element == EL_SPRING && level.use_spring_bug))
14959 return MP_NO_ACTION;
14961 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14962 ((move_direction & MV_VERTICAL &&
14963 ((element_info[element].move_pattern & MV_LEFT &&
14964 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14965 (element_info[element].move_pattern & MV_RIGHT &&
14966 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14967 (move_direction & MV_HORIZONTAL &&
14968 ((element_info[element].move_pattern & MV_UP &&
14969 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14970 (element_info[element].move_pattern & MV_DOWN &&
14971 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14972 return MP_NO_ACTION;
14974 /* do not push elements already moving away faster than player */
14975 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14976 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14977 return MP_NO_ACTION;
14979 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14981 if (player->push_delay_value == -1 || !player_was_pushing)
14982 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14984 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14986 if (player->push_delay_value == -1)
14987 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14989 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14991 if (!player->is_pushing)
14992 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14995 player->is_pushing = TRUE;
14996 player->is_active = TRUE;
14998 if (!(IN_LEV_FIELD(nextx, nexty) &&
14999 (IS_FREE(nextx, nexty) ||
15000 (IS_SB_ELEMENT(element) &&
15001 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15002 (IS_CUSTOM_ELEMENT(element) &&
15003 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15004 return MP_NO_ACTION;
15006 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15007 return MP_NO_ACTION;
15009 if (player->push_delay == -1) /* new pushing; restart delay */
15010 player->push_delay = 0;
15012 if (player->push_delay < player->push_delay_value &&
15013 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15014 element != EL_SPRING && element != EL_BALLOON)
15016 /* make sure that there is no move delay before next try to push */
15017 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15018 player->move_delay = 0;
15020 return MP_NO_ACTION;
15023 if (IS_CUSTOM_ELEMENT(element) &&
15024 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15026 if (!DigFieldByCE(nextx, nexty, element))
15027 return MP_NO_ACTION;
15030 if (IS_SB_ELEMENT(element))
15032 if (element == EL_SOKOBAN_FIELD_FULL)
15034 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15035 local_player->sokobanfields_still_needed++;
15038 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15040 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15041 local_player->sokobanfields_still_needed--;
15044 Feld[x][y] = EL_SOKOBAN_OBJECT;
15046 if (Back[x][y] == Back[nextx][nexty])
15047 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15048 else if (Back[x][y] != 0)
15049 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15052 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15055 if (local_player->sokobanfields_still_needed == 0 &&
15056 game.emulation == EMU_SOKOBAN)
15058 PlayerWins(player);
15060 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15064 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15066 InitMovingField(x, y, move_direction);
15067 GfxAction[x][y] = ACTION_PUSHING;
15069 if (mode == DF_SNAP)
15070 ContinueMoving(x, y);
15072 MovPos[x][y] = (dx != 0 ? dx : dy);
15074 Pushed[x][y] = TRUE;
15075 Pushed[nextx][nexty] = TRUE;
15077 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15078 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15080 player->push_delay_value = -1; /* get new value later */
15082 /* check for element change _after_ element has been pushed */
15083 if (game.use_change_when_pushing_bug)
15085 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15086 player->index_bit, dig_side);
15087 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15088 player->index_bit, dig_side);
15091 else if (IS_SWITCHABLE(element))
15093 if (PLAYER_SWITCHING(player, x, y))
15095 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15096 player->index_bit, dig_side);
15101 player->is_switching = TRUE;
15102 player->switch_x = x;
15103 player->switch_y = y;
15105 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15107 if (element == EL_ROBOT_WHEEL)
15109 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15113 game.robot_wheel_active = TRUE;
15115 TEST_DrawLevelField(x, y);
15117 else if (element == EL_SP_TERMINAL)
15121 SCAN_PLAYFIELD(xx, yy)
15123 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15125 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15126 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15129 else if (IS_BELT_SWITCH(element))
15131 ToggleBeltSwitch(x, y);
15133 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15134 element == EL_SWITCHGATE_SWITCH_DOWN ||
15135 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15136 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15138 ToggleSwitchgateSwitch(x, y);
15140 else if (element == EL_LIGHT_SWITCH ||
15141 element == EL_LIGHT_SWITCH_ACTIVE)
15143 ToggleLightSwitch(x, y);
15145 else if (element == EL_TIMEGATE_SWITCH ||
15146 element == EL_DC_TIMEGATE_SWITCH)
15148 ActivateTimegateSwitch(x, y);
15150 else if (element == EL_BALLOON_SWITCH_LEFT ||
15151 element == EL_BALLOON_SWITCH_RIGHT ||
15152 element == EL_BALLOON_SWITCH_UP ||
15153 element == EL_BALLOON_SWITCH_DOWN ||
15154 element == EL_BALLOON_SWITCH_NONE ||
15155 element == EL_BALLOON_SWITCH_ANY)
15157 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15158 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15159 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15160 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15161 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15164 else if (element == EL_LAMP)
15166 Feld[x][y] = EL_LAMP_ACTIVE;
15167 local_player->lights_still_needed--;
15169 ResetGfxAnimation(x, y);
15170 TEST_DrawLevelField(x, y);
15172 else if (element == EL_TIME_ORB_FULL)
15174 Feld[x][y] = EL_TIME_ORB_EMPTY;
15176 if (level.time > 0 || level.use_time_orb_bug)
15178 TimeLeft += level.time_orb_time;
15181 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15183 DisplayGameControlValues();
15185 DrawGameValue_Time(TimeLeft);
15189 ResetGfxAnimation(x, y);
15190 TEST_DrawLevelField(x, y);
15192 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15193 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15197 game.ball_state = !game.ball_state;
15199 SCAN_PLAYFIELD(xx, yy)
15201 int e = Feld[xx][yy];
15203 if (game.ball_state)
15205 if (e == EL_EMC_MAGIC_BALL)
15206 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15207 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15208 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15212 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15213 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15214 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15215 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15221 player->index_bit, dig_side);
15223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15224 player->index_bit, dig_side);
15226 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15227 player->index_bit, dig_side);
15233 if (!PLAYER_SWITCHING(player, x, y))
15235 player->is_switching = TRUE;
15236 player->switch_x = x;
15237 player->switch_y = y;
15239 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15240 player->index_bit, dig_side);
15241 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15242 player->index_bit, dig_side);
15244 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15245 player->index_bit, dig_side);
15246 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15247 player->index_bit, dig_side);
15250 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15251 player->index_bit, dig_side);
15252 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15253 player->index_bit, dig_side);
15255 return MP_NO_ACTION;
15258 player->push_delay = -1;
15260 if (is_player) /* function can also be called by EL_PENGUIN */
15262 if (Feld[x][y] != element) /* really digged/collected something */
15264 player->is_collecting = !player->is_digging;
15265 player->is_active = TRUE;
15272 static boolean DigFieldByCE(int x, int y, int digging_element)
15274 int element = Feld[x][y];
15276 if (!IS_FREE(x, y))
15278 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15279 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15282 /* no element can dig solid indestructible elements */
15283 if (IS_INDESTRUCTIBLE(element) &&
15284 !IS_DIGGABLE(element) &&
15285 !IS_COLLECTIBLE(element))
15288 if (AmoebaNr[x][y] &&
15289 (element == EL_AMOEBA_FULL ||
15290 element == EL_BD_AMOEBA ||
15291 element == EL_AMOEBA_GROWING))
15293 AmoebaCnt[AmoebaNr[x][y]]--;
15294 AmoebaCnt2[AmoebaNr[x][y]]--;
15297 if (IS_MOVING(x, y))
15298 RemoveMovingField(x, y);
15302 TEST_DrawLevelField(x, y);
15305 /* if digged element was about to explode, prevent the explosion */
15306 ExplodeField[x][y] = EX_TYPE_NONE;
15308 PlayLevelSoundAction(x, y, action);
15311 Store[x][y] = EL_EMPTY;
15314 /* this makes it possible to leave the removed element again */
15315 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15316 Store[x][y] = element;
15318 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15320 int move_leave_element = element_info[digging_element].move_leave_element;
15322 /* this makes it possible to leave the removed element again */
15323 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15324 element : move_leave_element);
15331 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15333 int jx = player->jx, jy = player->jy;
15334 int x = jx + dx, y = jy + dy;
15335 int snap_direction = (dx == -1 ? MV_LEFT :
15336 dx == +1 ? MV_RIGHT :
15338 dy == +1 ? MV_DOWN : MV_NONE);
15339 boolean can_continue_snapping = (level.continuous_snapping &&
15340 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15342 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15345 if (!player->active || !IN_LEV_FIELD(x, y))
15353 if (player->MovPos == 0)
15354 player->is_pushing = FALSE;
15356 player->is_snapping = FALSE;
15358 if (player->MovPos == 0)
15360 player->is_moving = FALSE;
15361 player->is_digging = FALSE;
15362 player->is_collecting = FALSE;
15368 #if USE_NEW_CONTINUOUS_SNAPPING
15369 /* prevent snapping with already pressed snap key when not allowed */
15370 if (player->is_snapping && !can_continue_snapping)
15373 if (player->is_snapping)
15377 player->MovDir = snap_direction;
15379 if (player->MovPos == 0)
15381 player->is_moving = FALSE;
15382 player->is_digging = FALSE;
15383 player->is_collecting = FALSE;
15386 player->is_dropping = FALSE;
15387 player->is_dropping_pressed = FALSE;
15388 player->drop_pressed_delay = 0;
15390 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15393 player->is_snapping = TRUE;
15394 player->is_active = TRUE;
15396 if (player->MovPos == 0)
15398 player->is_moving = FALSE;
15399 player->is_digging = FALSE;
15400 player->is_collecting = FALSE;
15403 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15404 TEST_DrawLevelField(player->last_jx, player->last_jy);
15406 TEST_DrawLevelField(x, y);
15411 static boolean DropElement(struct PlayerInfo *player)
15413 int old_element, new_element;
15414 int dropx = player->jx, dropy = player->jy;
15415 int drop_direction = player->MovDir;
15416 int drop_side = drop_direction;
15418 int drop_element = get_next_dropped_element(player);
15420 int drop_element = (player->inventory_size > 0 ?
15421 player->inventory_element[player->inventory_size - 1] :
15422 player->inventory_infinite_element != EL_UNDEFINED ?
15423 player->inventory_infinite_element :
15424 player->dynabombs_left > 0 ?
15425 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15429 player->is_dropping_pressed = TRUE;
15431 /* do not drop an element on top of another element; when holding drop key
15432 pressed without moving, dropped element must move away before the next
15433 element can be dropped (this is especially important if the next element
15434 is dynamite, which can be placed on background for historical reasons) */
15435 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15438 if (IS_THROWABLE(drop_element))
15440 dropx += GET_DX_FROM_DIR(drop_direction);
15441 dropy += GET_DY_FROM_DIR(drop_direction);
15443 if (!IN_LEV_FIELD(dropx, dropy))
15447 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15448 new_element = drop_element; /* default: no change when dropping */
15450 /* check if player is active, not moving and ready to drop */
15451 if (!player->active || player->MovPos || player->drop_delay > 0)
15454 /* check if player has anything that can be dropped */
15455 if (new_element == EL_UNDEFINED)
15458 /* check if drop key was pressed long enough for EM style dynamite */
15459 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15462 /* check if anything can be dropped at the current position */
15463 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15466 /* collected custom elements can only be dropped on empty fields */
15467 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15470 if (old_element != EL_EMPTY)
15471 Back[dropx][dropy] = old_element; /* store old element on this field */
15473 ResetGfxAnimation(dropx, dropy);
15474 ResetRandomAnimationValue(dropx, dropy);
15476 if (player->inventory_size > 0 ||
15477 player->inventory_infinite_element != EL_UNDEFINED)
15479 if (player->inventory_size > 0)
15481 player->inventory_size--;
15483 DrawGameDoorValues();
15485 if (new_element == EL_DYNAMITE)
15486 new_element = EL_DYNAMITE_ACTIVE;
15487 else if (new_element == EL_EM_DYNAMITE)
15488 new_element = EL_EM_DYNAMITE_ACTIVE;
15489 else if (new_element == EL_SP_DISK_RED)
15490 new_element = EL_SP_DISK_RED_ACTIVE;
15493 Feld[dropx][dropy] = new_element;
15495 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15496 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15497 el2img(Feld[dropx][dropy]), 0);
15499 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15501 /* needed if previous element just changed to "empty" in the last frame */
15502 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15504 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15505 player->index_bit, drop_side);
15506 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15508 player->index_bit, drop_side);
15510 TestIfElementTouchesCustomElement(dropx, dropy);
15512 else /* player is dropping a dyna bomb */
15514 player->dynabombs_left--;
15516 Feld[dropx][dropy] = new_element;
15518 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15519 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15520 el2img(Feld[dropx][dropy]), 0);
15522 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15525 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15526 InitField_WithBug1(dropx, dropy, FALSE);
15528 new_element = Feld[dropx][dropy]; /* element might have changed */
15530 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15531 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15533 int move_direction, nextx, nexty;
15535 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15536 MovDir[dropx][dropy] = drop_direction;
15538 move_direction = MovDir[dropx][dropy];
15539 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15540 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15542 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15544 #if USE_FIX_IMPACT_COLLISION
15545 /* do not cause impact style collision by dropping elements that can fall */
15546 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15548 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15552 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15553 player->is_dropping = TRUE;
15555 player->drop_pressed_delay = 0;
15556 player->is_dropping_pressed = FALSE;
15558 player->drop_x = dropx;
15559 player->drop_y = dropy;
15564 /* ------------------------------------------------------------------------- */
15565 /* game sound playing functions */
15566 /* ------------------------------------------------------------------------- */
15568 static int *loop_sound_frame = NULL;
15569 static int *loop_sound_volume = NULL;
15571 void InitPlayLevelSound()
15573 int num_sounds = getSoundListSize();
15575 checked_free(loop_sound_frame);
15576 checked_free(loop_sound_volume);
15578 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15579 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15582 static void PlayLevelSound(int x, int y, int nr)
15584 int sx = SCREENX(x), sy = SCREENY(y);
15585 int volume, stereo_position;
15586 int max_distance = 8;
15587 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15589 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15590 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15593 if (!IN_LEV_FIELD(x, y) ||
15594 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15595 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15598 volume = SOUND_MAX_VOLUME;
15600 if (!IN_SCR_FIELD(sx, sy))
15602 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15603 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15605 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15608 stereo_position = (SOUND_MAX_LEFT +
15609 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15610 (SCR_FIELDX + 2 * max_distance));
15612 if (IS_LOOP_SOUND(nr))
15614 /* This assures that quieter loop sounds do not overwrite louder ones,
15615 while restarting sound volume comparison with each new game frame. */
15617 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15620 loop_sound_volume[nr] = volume;
15621 loop_sound_frame[nr] = FrameCounter;
15624 PlaySoundExt(nr, volume, stereo_position, type);
15627 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15629 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15630 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15631 y < LEVELY(BY1) ? LEVELY(BY1) :
15632 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15636 static void PlayLevelSoundAction(int x, int y, int action)
15638 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15641 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15643 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15645 if (sound_effect != SND_UNDEFINED)
15646 PlayLevelSound(x, y, sound_effect);
15649 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15652 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15654 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15655 PlayLevelSound(x, y, sound_effect);
15658 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15660 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15662 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15663 PlayLevelSound(x, y, sound_effect);
15666 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15668 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15670 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15671 StopSound(sound_effect);
15674 static void PlayLevelMusic()
15676 if (levelset.music[level_nr] != MUS_UNDEFINED)
15677 PlayMusic(levelset.music[level_nr]); /* from config file */
15679 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15682 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15684 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15685 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15686 int x = xx - 1 - offset;
15687 int y = yy - 1 - offset;
15692 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15696 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15700 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15704 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15708 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15712 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15716 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15719 case SAMPLE_android_clone:
15720 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15723 case SAMPLE_android_move:
15724 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15727 case SAMPLE_spring:
15728 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15732 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15736 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15739 case SAMPLE_eater_eat:
15740 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15744 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15747 case SAMPLE_collect:
15748 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15751 case SAMPLE_diamond:
15752 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15755 case SAMPLE_squash:
15756 /* !!! CHECK THIS !!! */
15758 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15760 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15764 case SAMPLE_wonderfall:
15765 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15769 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15773 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15777 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15781 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15785 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15789 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15792 case SAMPLE_wonder:
15793 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15797 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15800 case SAMPLE_exit_open:
15801 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15804 case SAMPLE_exit_leave:
15805 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15808 case SAMPLE_dynamite:
15809 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15813 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15817 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15821 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15825 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15829 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15833 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15837 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15843 void ChangeTime(int value)
15845 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15849 /* EMC game engine uses value from time counter of RND game engine */
15850 level.native_em_level->lev->time = *time;
15852 DrawGameValue_Time(*time);
15855 void RaiseScore(int value)
15857 /* EMC game engine and RND game engine have separate score counters */
15858 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15859 &level.native_em_level->lev->score : &local_player->score);
15863 DrawGameValue_Score(*score);
15867 void RaiseScore(int value)
15869 local_player->score += value;
15872 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15874 DisplayGameControlValues();
15876 DrawGameValue_Score(local_player->score);
15880 void RaiseScoreElement(int element)
15885 case EL_BD_DIAMOND:
15886 case EL_EMERALD_YELLOW:
15887 case EL_EMERALD_RED:
15888 case EL_EMERALD_PURPLE:
15889 case EL_SP_INFOTRON:
15890 RaiseScore(level.score[SC_EMERALD]);
15893 RaiseScore(level.score[SC_DIAMOND]);
15896 RaiseScore(level.score[SC_CRYSTAL]);
15899 RaiseScore(level.score[SC_PEARL]);
15902 case EL_BD_BUTTERFLY:
15903 case EL_SP_ELECTRON:
15904 RaiseScore(level.score[SC_BUG]);
15907 case EL_BD_FIREFLY:
15908 case EL_SP_SNIKSNAK:
15909 RaiseScore(level.score[SC_SPACESHIP]);
15912 case EL_DARK_YAMYAM:
15913 RaiseScore(level.score[SC_YAMYAM]);
15916 RaiseScore(level.score[SC_ROBOT]);
15919 RaiseScore(level.score[SC_PACMAN]);
15922 RaiseScore(level.score[SC_NUT]);
15925 case EL_EM_DYNAMITE:
15926 case EL_SP_DISK_RED:
15927 case EL_DYNABOMB_INCREASE_NUMBER:
15928 case EL_DYNABOMB_INCREASE_SIZE:
15929 case EL_DYNABOMB_INCREASE_POWER:
15930 RaiseScore(level.score[SC_DYNAMITE]);
15932 case EL_SHIELD_NORMAL:
15933 case EL_SHIELD_DEADLY:
15934 RaiseScore(level.score[SC_SHIELD]);
15936 case EL_EXTRA_TIME:
15937 RaiseScore(level.extra_time_score);
15951 case EL_DC_KEY_WHITE:
15952 RaiseScore(level.score[SC_KEY]);
15955 RaiseScore(element_info[element].collect_score);
15960 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15962 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15964 #if defined(NETWORK_AVALIABLE)
15965 if (options.network)
15966 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15975 FadeSkipNextFadeIn();
15977 fading = fading_none;
15981 OpenDoor(DOOR_CLOSE_1);
15984 game_status = GAME_MODE_MAIN;
15987 DrawAndFadeInMainMenu(REDRAW_FIELD);
15995 FadeOut(REDRAW_FIELD);
15998 game_status = GAME_MODE_MAIN;
16000 DrawAndFadeInMainMenu(REDRAW_FIELD);
16004 else /* continue playing the game */
16006 if (tape.playing && tape.deactivate_display)
16007 TapeDeactivateDisplayOff(TRUE);
16009 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16011 if (tape.playing && tape.deactivate_display)
16012 TapeDeactivateDisplayOn();
16016 void RequestQuitGame(boolean ask_if_really_quit)
16018 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16019 boolean skip_request = AllPlayersGone || quick_quit;
16021 RequestQuitGameExt(skip_request, quick_quit,
16022 "Do you really want to quit the game ?");
16026 /* ------------------------------------------------------------------------- */
16027 /* random generator functions */
16028 /* ------------------------------------------------------------------------- */
16030 unsigned int InitEngineRandom_RND(long seed)
16032 game.num_random_calls = 0;
16035 unsigned int rnd_seed = InitEngineRandom(seed);
16037 printf("::: START RND: %d\n", rnd_seed);
16042 return InitEngineRandom(seed);
16048 unsigned int RND(int max)
16052 game.num_random_calls++;
16054 return GetEngineRandom(max);
16061 /* ------------------------------------------------------------------------- */
16062 /* game engine snapshot handling functions */
16063 /* ------------------------------------------------------------------------- */
16065 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16067 struct EngineSnapshotInfo
16069 /* runtime values for custom element collect score */
16070 int collect_score[NUM_CUSTOM_ELEMENTS];
16072 /* runtime values for group element choice position */
16073 int choice_pos[NUM_GROUP_ELEMENTS];
16075 /* runtime values for belt position animations */
16076 int belt_graphic[4 * NUM_BELT_PARTS];
16077 int belt_anim_mode[4 * NUM_BELT_PARTS];
16080 struct EngineSnapshotNodeInfo
16087 static struct EngineSnapshotInfo engine_snapshot_rnd;
16088 static ListNode *engine_snapshot_list = NULL;
16089 static char *snapshot_level_identifier = NULL;
16090 static int snapshot_level_nr = -1;
16092 void FreeEngineSnapshot()
16094 while (engine_snapshot_list != NULL)
16095 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16098 setString(&snapshot_level_identifier, NULL);
16099 snapshot_level_nr = -1;
16102 static void SaveEngineSnapshotValues_RND()
16104 static int belt_base_active_element[4] =
16106 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16107 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16108 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16109 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16113 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16115 int element = EL_CUSTOM_START + i;
16117 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16120 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16122 int element = EL_GROUP_START + i;
16124 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16127 for (i = 0; i < 4; i++)
16129 for (j = 0; j < NUM_BELT_PARTS; j++)
16131 int element = belt_base_active_element[i] + j;
16132 int graphic = el2img(element);
16133 int anim_mode = graphic_info[graphic].anim_mode;
16135 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16136 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16141 static void LoadEngineSnapshotValues_RND()
16143 unsigned long num_random_calls = game.num_random_calls;
16146 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16148 int element = EL_CUSTOM_START + i;
16150 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16153 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16155 int element = EL_GROUP_START + i;
16157 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16160 for (i = 0; i < 4; i++)
16162 for (j = 0; j < NUM_BELT_PARTS; j++)
16164 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16165 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16167 graphic_info[graphic].anim_mode = anim_mode;
16171 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16173 InitRND(tape.random_seed);
16174 for (i = 0; i < num_random_calls; i++)
16178 if (game.num_random_calls != num_random_calls)
16180 Error(ERR_INFO, "number of random calls out of sync");
16181 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16182 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16183 Error(ERR_EXIT, "this should not happen -- please debug");
16187 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16189 struct EngineSnapshotNodeInfo *bi =
16190 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16192 bi->buffer_orig = buffer;
16193 bi->buffer_copy = checked_malloc(size);
16196 memcpy(bi->buffer_copy, buffer, size);
16198 addNodeToList(&engine_snapshot_list, NULL, bi);
16201 void SaveEngineSnapshot()
16203 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16205 if (level_editor_test_game) /* do not save snapshots from editor */
16208 /* copy some special values to a structure better suited for the snapshot */
16210 SaveEngineSnapshotValues_RND();
16211 SaveEngineSnapshotValues_EM();
16213 /* save values stored in special snapshot structure */
16215 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16216 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16218 /* save further RND engine values */
16220 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16221 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16222 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16224 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16225 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16226 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16227 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16229 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16230 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16231 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16232 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16233 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16235 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16236 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16237 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16239 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16241 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16243 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16244 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16246 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16247 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16248 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16249 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16250 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16251 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16252 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16253 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16254 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16255 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16256 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16257 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16258 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16259 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16260 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16261 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16262 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16263 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16265 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16266 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16268 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16269 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16270 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16272 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16273 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16275 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16276 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16277 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16278 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16279 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16281 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16282 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16284 /* save level identification information */
16286 setString(&snapshot_level_identifier, leveldir_current->identifier);
16287 snapshot_level_nr = level_nr;
16290 ListNode *node = engine_snapshot_list;
16293 while (node != NULL)
16295 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16300 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16304 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16306 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16309 void LoadEngineSnapshot()
16311 ListNode *node = engine_snapshot_list;
16313 if (engine_snapshot_list == NULL)
16316 while (node != NULL)
16318 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16323 /* restore special values from snapshot structure */
16325 LoadEngineSnapshotValues_RND();
16326 LoadEngineSnapshotValues_EM();
16329 boolean CheckEngineSnapshot()
16331 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16332 snapshot_level_nr == level_nr);
16336 /* ---------- new game button stuff ---------------------------------------- */
16338 /* graphic position values for game buttons */
16339 #define GAME_BUTTON_XSIZE 30
16340 #define GAME_BUTTON_YSIZE 30
16341 #define GAME_BUTTON_XPOS 5
16342 #define GAME_BUTTON_YPOS 215
16343 #define SOUND_BUTTON_XPOS 5
16344 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16346 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16347 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16348 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16349 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16350 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16351 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16359 } gamebutton_info[NUM_GAME_BUTTONS] =
16363 &game.button.stop.x, &game.button.stop.y,
16364 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16369 &game.button.pause.x, &game.button.pause.y,
16370 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16371 GAME_CTRL_ID_PAUSE,
16375 &game.button.play.x, &game.button.play.y,
16376 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16381 &game.button.sound_music.x, &game.button.sound_music.y,
16382 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16383 SOUND_CTRL_ID_MUSIC,
16384 "background music on/off"
16387 &game.button.sound_loops.x, &game.button.sound_loops.y,
16388 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16389 SOUND_CTRL_ID_LOOPS,
16390 "sound loops on/off"
16393 &game.button.sound_simple.x,&game.button.sound_simple.y,
16394 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16395 SOUND_CTRL_ID_SIMPLE,
16396 "normal sounds on/off"
16400 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16405 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16406 GAME_CTRL_ID_PAUSE,
16410 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16415 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16416 SOUND_CTRL_ID_MUSIC,
16417 "background music on/off"
16420 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16421 SOUND_CTRL_ID_LOOPS,
16422 "sound loops on/off"
16425 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16426 SOUND_CTRL_ID_SIMPLE,
16427 "normal sounds on/off"
16432 void CreateGameButtons()
16436 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16438 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16439 struct GadgetInfo *gi;
16442 unsigned long event_mask;
16444 int gd_xoffset, gd_yoffset;
16445 int gd_x1, gd_x2, gd_y1, gd_y2;
16448 x = DX + *gamebutton_info[i].x;
16449 y = DY + *gamebutton_info[i].y;
16450 gd_xoffset = gamebutton_info[i].gd_x;
16451 gd_yoffset = gamebutton_info[i].gd_y;
16452 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16453 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16455 if (id == GAME_CTRL_ID_STOP ||
16456 id == GAME_CTRL_ID_PAUSE ||
16457 id == GAME_CTRL_ID_PLAY)
16459 button_type = GD_TYPE_NORMAL_BUTTON;
16461 event_mask = GD_EVENT_RELEASED;
16462 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16463 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16467 button_type = GD_TYPE_CHECK_BUTTON;
16469 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16470 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16471 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16472 event_mask = GD_EVENT_PRESSED;
16473 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16474 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16477 gi = CreateGadget(GDI_CUSTOM_ID, id,
16478 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16483 GDI_X, DX + gd_xoffset,
16484 GDI_Y, DY + gd_yoffset,
16486 GDI_WIDTH, GAME_BUTTON_XSIZE,
16487 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16488 GDI_TYPE, button_type,
16489 GDI_STATE, GD_BUTTON_UNPRESSED,
16490 GDI_CHECKED, checked,
16491 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16492 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16493 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16494 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16495 GDI_DIRECT_DRAW, FALSE,
16496 GDI_EVENT_MASK, event_mask,
16497 GDI_CALLBACK_ACTION, HandleGameButtons,
16501 Error(ERR_EXIT, "cannot create gadget");
16503 game_gadget[id] = gi;
16507 void FreeGameButtons()
16511 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16512 FreeGadget(game_gadget[i]);
16515 static void MapGameButtons()
16519 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16520 MapGadget(game_gadget[i]);
16523 void UnmapGameButtons()
16527 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16528 UnmapGadget(game_gadget[i]);
16531 void RedrawGameButtons()
16535 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16536 RedrawGadget(game_gadget[i]);
16539 static void HandleGameButtons(struct GadgetInfo *gi)
16541 int id = gi->custom_id;
16543 if (game_status != GAME_MODE_PLAYING)
16548 case GAME_CTRL_ID_STOP:
16552 RequestQuitGame(TRUE);
16555 case GAME_CTRL_ID_PAUSE:
16556 if (options.network)
16558 #if defined(NETWORK_AVALIABLE)
16560 SendToServer_ContinuePlaying();
16562 SendToServer_PausePlaying();
16566 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16569 case GAME_CTRL_ID_PLAY:
16572 #if defined(NETWORK_AVALIABLE)
16573 if (options.network)
16574 SendToServer_ContinuePlaying();
16578 tape.pausing = FALSE;
16579 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16584 case SOUND_CTRL_ID_MUSIC:
16585 if (setup.sound_music)
16587 setup.sound_music = FALSE;
16590 else if (audio.music_available)
16592 setup.sound = setup.sound_music = TRUE;
16594 SetAudioMode(setup.sound);
16600 case SOUND_CTRL_ID_LOOPS:
16601 if (setup.sound_loops)
16602 setup.sound_loops = FALSE;
16603 else if (audio.loops_available)
16605 setup.sound = setup.sound_loops = TRUE;
16606 SetAudioMode(setup.sound);
16610 case SOUND_CTRL_ID_SIMPLE:
16611 if (setup.sound_simple)
16612 setup.sound_simple = FALSE;
16613 else if (audio.sound_available)
16615 setup.sound = setup.sound_simple = TRUE;
16616 SetAudioMode(setup.sound);