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 :
10178 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10181 if (action_arg_element_raw == EL_GROUP_START)
10182 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10185 int action_arg_direction =
10186 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10187 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10188 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10189 change->actual_trigger_side :
10190 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10191 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10194 int action_arg_number_min =
10195 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10198 int action_arg_number_max =
10199 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10200 action_type == CA_SET_LEVEL_GEMS ? 999 :
10201 action_type == CA_SET_LEVEL_TIME ? 9999 :
10202 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10203 action_type == CA_SET_CE_VALUE ? 9999 :
10204 action_type == CA_SET_CE_SCORE ? 9999 :
10207 int action_arg_number_reset =
10208 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10209 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10210 action_type == CA_SET_LEVEL_TIME ? level.time :
10211 action_type == CA_SET_LEVEL_SCORE ? 0 :
10212 #if USE_NEW_CUSTOM_VALUE
10213 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10215 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10217 action_type == CA_SET_CE_SCORE ? 0 :
10220 int action_arg_number =
10221 (action_arg <= CA_ARG_MAX ? action_arg :
10222 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10223 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10224 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10225 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10226 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10227 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10228 #if USE_NEW_CUSTOM_VALUE
10229 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10231 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10233 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10234 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10235 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10236 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10237 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10238 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10239 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10240 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10241 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10242 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10243 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10244 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10245 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10246 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10249 int action_arg_number_old =
10250 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10251 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10252 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10253 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10254 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10257 int action_arg_number_new =
10258 getModifiedActionNumber(action_arg_number_old,
10259 action_mode, action_arg_number,
10260 action_arg_number_min, action_arg_number_max);
10263 int trigger_player_bits =
10264 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10265 change->actual_trigger_player_bits : change->trigger_player);
10267 int trigger_player_bits =
10268 (change->actual_trigger_player >= EL_PLAYER_1 &&
10269 change->actual_trigger_player <= EL_PLAYER_4 ?
10270 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10274 int action_arg_player_bits =
10275 (action_arg >= CA_ARG_PLAYER_1 &&
10276 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10277 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10278 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10281 /* ---------- execute action -------------------------------------------- */
10283 switch (action_type)
10290 /* ---------- level actions ------------------------------------------- */
10292 case CA_RESTART_LEVEL:
10294 game.restart_level = TRUE;
10299 case CA_SHOW_ENVELOPE:
10301 int element = getSpecialActionElement(action_arg_element,
10302 action_arg_number, EL_ENVELOPE_1);
10304 if (IS_ENVELOPE(element))
10305 local_player->show_envelope = element;
10310 case CA_SET_LEVEL_TIME:
10312 if (level.time > 0) /* only modify limited time value */
10314 TimeLeft = action_arg_number_new;
10317 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10319 DisplayGameControlValues();
10321 DrawGameValue_Time(TimeLeft);
10324 if (!TimeLeft && setup.time_limit)
10325 for (i = 0; i < MAX_PLAYERS; i++)
10326 KillPlayer(&stored_player[i]);
10332 case CA_SET_LEVEL_SCORE:
10334 local_player->score = action_arg_number_new;
10337 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10339 DisplayGameControlValues();
10341 DrawGameValue_Score(local_player->score);
10347 case CA_SET_LEVEL_GEMS:
10349 local_player->gems_still_needed = action_arg_number_new;
10352 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10354 DisplayGameControlValues();
10356 DrawGameValue_Emeralds(local_player->gems_still_needed);
10362 #if !USE_PLAYER_GRAVITY
10363 case CA_SET_LEVEL_GRAVITY:
10365 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10366 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10367 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10373 case CA_SET_LEVEL_WIND:
10375 game.wind_direction = action_arg_direction;
10380 /* ---------- player actions ------------------------------------------ */
10382 case CA_MOVE_PLAYER:
10384 /* automatically move to the next field in specified direction */
10385 for (i = 0; i < MAX_PLAYERS; i++)
10386 if (trigger_player_bits & (1 << i))
10387 stored_player[i].programmed_action = action_arg_direction;
10392 case CA_EXIT_PLAYER:
10394 for (i = 0; i < MAX_PLAYERS; i++)
10395 if (action_arg_player_bits & (1 << i))
10396 PlayerWins(&stored_player[i]);
10401 case CA_KILL_PLAYER:
10403 for (i = 0; i < MAX_PLAYERS; i++)
10404 if (action_arg_player_bits & (1 << i))
10405 KillPlayer(&stored_player[i]);
10410 case CA_SET_PLAYER_KEYS:
10412 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10413 int element = getSpecialActionElement(action_arg_element,
10414 action_arg_number, EL_KEY_1);
10416 if (IS_KEY(element))
10418 for (i = 0; i < MAX_PLAYERS; i++)
10420 if (trigger_player_bits & (1 << i))
10422 stored_player[i].key[KEY_NR(element)] = key_state;
10424 DrawGameDoorValues();
10432 case CA_SET_PLAYER_SPEED:
10435 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10438 for (i = 0; i < MAX_PLAYERS; i++)
10440 if (trigger_player_bits & (1 << i))
10442 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10444 if (action_arg == CA_ARG_SPEED_FASTER &&
10445 stored_player[i].cannot_move)
10447 action_arg_number = STEPSIZE_VERY_SLOW;
10449 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10450 action_arg == CA_ARG_SPEED_FASTER)
10452 action_arg_number = 2;
10453 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10456 else if (action_arg == CA_ARG_NUMBER_RESET)
10458 action_arg_number = level.initial_player_stepsize[i];
10462 getModifiedActionNumber(move_stepsize,
10465 action_arg_number_min,
10466 action_arg_number_max);
10468 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10475 case CA_SET_PLAYER_SHIELD:
10477 for (i = 0; i < MAX_PLAYERS; i++)
10479 if (trigger_player_bits & (1 << i))
10481 if (action_arg == CA_ARG_SHIELD_OFF)
10483 stored_player[i].shield_normal_time_left = 0;
10484 stored_player[i].shield_deadly_time_left = 0;
10486 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10488 stored_player[i].shield_normal_time_left = 999999;
10490 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10492 stored_player[i].shield_normal_time_left = 999999;
10493 stored_player[i].shield_deadly_time_left = 999999;
10501 #if USE_PLAYER_GRAVITY
10502 case CA_SET_PLAYER_GRAVITY:
10504 for (i = 0; i < MAX_PLAYERS; i++)
10506 if (trigger_player_bits & (1 << i))
10508 stored_player[i].gravity =
10509 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10510 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10511 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10512 stored_player[i].gravity);
10520 case CA_SET_PLAYER_ARTWORK:
10522 for (i = 0; i < MAX_PLAYERS; i++)
10524 if (trigger_player_bits & (1 << i))
10526 int artwork_element = action_arg_element;
10528 if (action_arg == CA_ARG_ELEMENT_RESET)
10530 (level.use_artwork_element[i] ? level.artwork_element[i] :
10531 stored_player[i].element_nr);
10533 #if USE_GFX_RESET_PLAYER_ARTWORK
10534 if (stored_player[i].artwork_element != artwork_element)
10535 stored_player[i].Frame = 0;
10538 stored_player[i].artwork_element = artwork_element;
10540 SetPlayerWaiting(&stored_player[i], FALSE);
10542 /* set number of special actions for bored and sleeping animation */
10543 stored_player[i].num_special_action_bored =
10544 get_num_special_action(artwork_element,
10545 ACTION_BORING_1, ACTION_BORING_LAST);
10546 stored_player[i].num_special_action_sleeping =
10547 get_num_special_action(artwork_element,
10548 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10555 case CA_SET_PLAYER_INVENTORY:
10557 for (i = 0; i < MAX_PLAYERS; i++)
10559 struct PlayerInfo *player = &stored_player[i];
10562 if (trigger_player_bits & (1 << i))
10564 int inventory_element = action_arg_element;
10566 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10567 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10568 action_arg == CA_ARG_ELEMENT_ACTION)
10570 int element = inventory_element;
10571 int collect_count = element_info[element].collect_count_initial;
10573 if (!IS_CUSTOM_ELEMENT(element))
10576 if (collect_count == 0)
10577 player->inventory_infinite_element = element;
10579 for (k = 0; k < collect_count; k++)
10580 if (player->inventory_size < MAX_INVENTORY_SIZE)
10581 player->inventory_element[player->inventory_size++] =
10584 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10585 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10586 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10588 if (player->inventory_infinite_element != EL_UNDEFINED &&
10589 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10590 action_arg_element_raw))
10591 player->inventory_infinite_element = EL_UNDEFINED;
10593 for (k = 0, j = 0; j < player->inventory_size; j++)
10595 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10596 action_arg_element_raw))
10597 player->inventory_element[k++] = player->inventory_element[j];
10600 player->inventory_size = k;
10602 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10604 if (player->inventory_size > 0)
10606 for (j = 0; j < player->inventory_size - 1; j++)
10607 player->inventory_element[j] = player->inventory_element[j + 1];
10609 player->inventory_size--;
10612 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10614 if (player->inventory_size > 0)
10615 player->inventory_size--;
10617 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10619 player->inventory_infinite_element = EL_UNDEFINED;
10620 player->inventory_size = 0;
10622 else if (action_arg == CA_ARG_ELEMENT_RESET)
10624 player->inventory_infinite_element = EL_UNDEFINED;
10625 player->inventory_size = 0;
10627 if (level.use_initial_inventory[i])
10629 for (j = 0; j < level.initial_inventory_size[i]; j++)
10631 int element = level.initial_inventory_content[i][j];
10632 int collect_count = element_info[element].collect_count_initial;
10634 if (!IS_CUSTOM_ELEMENT(element))
10637 if (collect_count == 0)
10638 player->inventory_infinite_element = element;
10640 for (k = 0; k < collect_count; k++)
10641 if (player->inventory_size < MAX_INVENTORY_SIZE)
10642 player->inventory_element[player->inventory_size++] =
10653 /* ---------- CE actions ---------------------------------------------- */
10655 case CA_SET_CE_VALUE:
10657 #if USE_NEW_CUSTOM_VALUE
10658 int last_ce_value = CustomValue[x][y];
10660 CustomValue[x][y] = action_arg_number_new;
10662 if (CustomValue[x][y] != last_ce_value)
10664 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10665 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10667 if (CustomValue[x][y] == 0)
10669 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10670 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10678 case CA_SET_CE_SCORE:
10680 #if USE_NEW_CUSTOM_VALUE
10681 int last_ce_score = ei->collect_score;
10683 ei->collect_score = action_arg_number_new;
10685 if (ei->collect_score != last_ce_score)
10687 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10688 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10690 if (ei->collect_score == 0)
10694 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10695 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10698 This is a very special case that seems to be a mixture between
10699 CheckElementChange() and CheckTriggeredElementChange(): while
10700 the first one only affects single elements that are triggered
10701 directly, the second one affects multiple elements in the playfield
10702 that are triggered indirectly by another element. This is a third
10703 case: Changing the CE score always affects multiple identical CEs,
10704 so every affected CE must be checked, not only the single CE for
10705 which the CE score was changed in the first place (as every instance
10706 of that CE shares the same CE score, and therefore also can change)!
10708 SCAN_PLAYFIELD(xx, yy)
10710 if (Feld[xx][yy] == element)
10711 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10712 CE_SCORE_GETS_ZERO);
10721 case CA_SET_CE_ARTWORK:
10723 int artwork_element = action_arg_element;
10724 boolean reset_frame = FALSE;
10727 if (action_arg == CA_ARG_ELEMENT_RESET)
10728 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10731 if (ei->gfx_element != artwork_element)
10732 reset_frame = TRUE;
10734 ei->gfx_element = artwork_element;
10736 SCAN_PLAYFIELD(xx, yy)
10738 if (Feld[xx][yy] == element)
10742 ResetGfxAnimation(xx, yy);
10743 ResetRandomAnimationValue(xx, yy);
10746 TEST_DrawLevelField(xx, yy);
10753 /* ---------- engine actions ------------------------------------------ */
10755 case CA_SET_ENGINE_SCAN_MODE:
10757 InitPlayfieldScanMode(action_arg);
10767 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10769 int old_element = Feld[x][y];
10770 int new_element = GetElementFromGroupElement(element);
10771 int previous_move_direction = MovDir[x][y];
10772 #if USE_NEW_CUSTOM_VALUE
10773 int last_ce_value = CustomValue[x][y];
10775 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10776 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10777 boolean add_player_onto_element = (new_element_is_player &&
10778 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10779 /* this breaks SnakeBite when a snake is
10780 halfway through a door that closes */
10781 /* NOW FIXED AT LEVEL INIT IN files.c */
10782 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10784 IS_WALKABLE(old_element));
10787 /* check if element under the player changes from accessible to unaccessible
10788 (needed for special case of dropping element which then changes) */
10789 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10790 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10798 if (!add_player_onto_element)
10800 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10801 RemoveMovingField(x, y);
10805 Feld[x][y] = new_element;
10807 #if !USE_GFX_RESET_GFX_ANIMATION
10808 ResetGfxAnimation(x, y);
10809 ResetRandomAnimationValue(x, y);
10812 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10813 MovDir[x][y] = previous_move_direction;
10815 #if USE_NEW_CUSTOM_VALUE
10816 if (element_info[new_element].use_last_ce_value)
10817 CustomValue[x][y] = last_ce_value;
10820 InitField_WithBug1(x, y, FALSE);
10822 new_element = Feld[x][y]; /* element may have changed */
10824 #if USE_GFX_RESET_GFX_ANIMATION
10825 ResetGfxAnimation(x, y);
10826 ResetRandomAnimationValue(x, y);
10829 TEST_DrawLevelField(x, y);
10831 if (GFX_CRUMBLED(new_element))
10832 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10836 /* check if element under the player changes from accessible to unaccessible
10837 (needed for special case of dropping element which then changes) */
10838 /* (must be checked after creating new element for walkable group elements) */
10839 #if USE_FIX_KILLED_BY_NON_WALKABLE
10840 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10841 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10848 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10849 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10858 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10859 if (new_element_is_player)
10860 RelocatePlayer(x, y, new_element);
10863 ChangeCount[x][y]++; /* count number of changes in the same frame */
10865 TestIfBadThingTouchesPlayer(x, y);
10866 TestIfPlayerTouchesCustomElement(x, y);
10867 TestIfElementTouchesCustomElement(x, y);
10870 static void CreateField(int x, int y, int element)
10872 CreateFieldExt(x, y, element, FALSE);
10875 static void CreateElementFromChange(int x, int y, int element)
10877 element = GET_VALID_RUNTIME_ELEMENT(element);
10879 #if USE_STOP_CHANGED_ELEMENTS
10880 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10882 int old_element = Feld[x][y];
10884 /* prevent changed element from moving in same engine frame
10885 unless both old and new element can either fall or move */
10886 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10887 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10892 CreateFieldExt(x, y, element, TRUE);
10895 static boolean ChangeElement(int x, int y, int element, int page)
10897 struct ElementInfo *ei = &element_info[element];
10898 struct ElementChangeInfo *change = &ei->change_page[page];
10899 int ce_value = CustomValue[x][y];
10900 int ce_score = ei->collect_score;
10901 int target_element;
10902 int old_element = Feld[x][y];
10904 /* always use default change event to prevent running into a loop */
10905 if (ChangeEvent[x][y] == -1)
10906 ChangeEvent[x][y] = CE_DELAY;
10908 if (ChangeEvent[x][y] == CE_DELAY)
10910 /* reset actual trigger element, trigger player and action element */
10911 change->actual_trigger_element = EL_EMPTY;
10912 change->actual_trigger_player = EL_EMPTY;
10913 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10914 change->actual_trigger_side = CH_SIDE_NONE;
10915 change->actual_trigger_ce_value = 0;
10916 change->actual_trigger_ce_score = 0;
10919 /* do not change elements more than a specified maximum number of changes */
10920 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10923 ChangeCount[x][y]++; /* count number of changes in the same frame */
10925 if (change->explode)
10932 if (change->use_target_content)
10934 boolean complete_replace = TRUE;
10935 boolean can_replace[3][3];
10938 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10941 boolean is_walkable;
10942 boolean is_diggable;
10943 boolean is_collectible;
10944 boolean is_removable;
10945 boolean is_destructible;
10946 int ex = x + xx - 1;
10947 int ey = y + yy - 1;
10948 int content_element = change->target_content.e[xx][yy];
10951 can_replace[xx][yy] = TRUE;
10953 if (ex == x && ey == y) /* do not check changing element itself */
10956 if (content_element == EL_EMPTY_SPACE)
10958 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10963 if (!IN_LEV_FIELD(ex, ey))
10965 can_replace[xx][yy] = FALSE;
10966 complete_replace = FALSE;
10973 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10974 e = MovingOrBlocked2Element(ex, ey);
10976 is_empty = (IS_FREE(ex, ey) ||
10977 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10979 is_walkable = (is_empty || IS_WALKABLE(e));
10980 is_diggable = (is_empty || IS_DIGGABLE(e));
10981 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10982 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10983 is_removable = (is_diggable || is_collectible);
10985 can_replace[xx][yy] =
10986 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10987 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10988 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10989 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10990 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10991 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10992 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10994 if (!can_replace[xx][yy])
10995 complete_replace = FALSE;
10998 if (!change->only_if_complete || complete_replace)
11000 boolean something_has_changed = FALSE;
11002 if (change->only_if_complete && change->use_random_replace &&
11003 RND(100) < change->random_percentage)
11006 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11008 int ex = x + xx - 1;
11009 int ey = y + yy - 1;
11010 int content_element;
11012 if (can_replace[xx][yy] && (!change->use_random_replace ||
11013 RND(100) < change->random_percentage))
11015 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11016 RemoveMovingField(ex, ey);
11018 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11020 content_element = change->target_content.e[xx][yy];
11021 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11022 ce_value, ce_score);
11024 CreateElementFromChange(ex, ey, target_element);
11026 something_has_changed = TRUE;
11028 /* for symmetry reasons, freeze newly created border elements */
11029 if (ex != x || ey != y)
11030 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11034 if (something_has_changed)
11036 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11037 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11043 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11044 ce_value, ce_score);
11046 if (element == EL_DIAGONAL_GROWING ||
11047 element == EL_DIAGONAL_SHRINKING)
11049 target_element = Store[x][y];
11051 Store[x][y] = EL_EMPTY;
11054 CreateElementFromChange(x, y, target_element);
11056 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11057 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11060 /* this uses direct change before indirect change */
11061 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11066 #if USE_NEW_DELAYED_ACTION
11068 static void HandleElementChange(int x, int y, int page)
11070 int element = MovingOrBlocked2Element(x, y);
11071 struct ElementInfo *ei = &element_info[element];
11072 struct ElementChangeInfo *change = &ei->change_page[page];
11075 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11076 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11079 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11080 x, y, element, element_info[element].token_name);
11081 printf("HandleElementChange(): This should never happen!\n");
11086 /* this can happen with classic bombs on walkable, changing elements */
11087 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11090 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11091 ChangeDelay[x][y] = 0;
11097 if (ChangeDelay[x][y] == 0) /* initialize element change */
11099 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11101 if (change->can_change)
11104 /* !!! not clear why graphic animation should be reset at all here !!! */
11105 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11106 #if USE_GFX_RESET_WHEN_NOT_MOVING
11107 /* when a custom element is about to change (for example by change delay),
11108 do not reset graphic animation when the custom element is moving */
11109 if (!IS_MOVING(x, y))
11112 ResetGfxAnimation(x, y);
11113 ResetRandomAnimationValue(x, y);
11117 if (change->pre_change_function)
11118 change->pre_change_function(x, y);
11122 ChangeDelay[x][y]--;
11124 if (ChangeDelay[x][y] != 0) /* continue element change */
11126 if (change->can_change)
11128 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11130 if (IS_ANIMATED(graphic))
11131 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11133 if (change->change_function)
11134 change->change_function(x, y);
11137 else /* finish element change */
11139 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11141 page = ChangePage[x][y];
11142 ChangePage[x][y] = -1;
11144 change = &ei->change_page[page];
11147 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11149 ChangeDelay[x][y] = 1; /* try change after next move step */
11150 ChangePage[x][y] = page; /* remember page to use for change */
11155 if (change->can_change)
11157 if (ChangeElement(x, y, element, page))
11159 if (change->post_change_function)
11160 change->post_change_function(x, y);
11164 if (change->has_action)
11165 ExecuteCustomElementAction(x, y, element, page);
11171 static void HandleElementChange(int x, int y, int page)
11173 int element = MovingOrBlocked2Element(x, y);
11174 struct ElementInfo *ei = &element_info[element];
11175 struct ElementChangeInfo *change = &ei->change_page[page];
11178 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11181 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11182 x, y, element, element_info[element].token_name);
11183 printf("HandleElementChange(): This should never happen!\n");
11188 /* this can happen with classic bombs on walkable, changing elements */
11189 if (!CAN_CHANGE(element))
11192 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11193 ChangeDelay[x][y] = 0;
11199 if (ChangeDelay[x][y] == 0) /* initialize element change */
11201 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11203 ResetGfxAnimation(x, y);
11204 ResetRandomAnimationValue(x, y);
11206 if (change->pre_change_function)
11207 change->pre_change_function(x, y);
11210 ChangeDelay[x][y]--;
11212 if (ChangeDelay[x][y] != 0) /* continue element change */
11214 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11216 if (IS_ANIMATED(graphic))
11217 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11219 if (change->change_function)
11220 change->change_function(x, y);
11222 else /* finish element change */
11224 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11226 page = ChangePage[x][y];
11227 ChangePage[x][y] = -1;
11229 change = &ei->change_page[page];
11232 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11234 ChangeDelay[x][y] = 1; /* try change after next move step */
11235 ChangePage[x][y] = page; /* remember page to use for change */
11240 if (ChangeElement(x, y, element, page))
11242 if (change->post_change_function)
11243 change->post_change_function(x, y);
11250 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11251 int trigger_element,
11253 int trigger_player,
11257 boolean change_done_any = FALSE;
11258 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11261 if (!(trigger_events[trigger_element][trigger_event]))
11265 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11266 trigger_event, recursion_loop_depth, recursion_loop_detected,
11267 recursion_loop_element, EL_NAME(recursion_loop_element));
11270 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11272 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11274 int element = EL_CUSTOM_START + i;
11275 boolean change_done = FALSE;
11278 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11279 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11282 for (p = 0; p < element_info[element].num_change_pages; p++)
11284 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11286 if (change->can_change_or_has_action &&
11287 change->has_event[trigger_event] &&
11288 change->trigger_side & trigger_side &&
11289 change->trigger_player & trigger_player &&
11290 change->trigger_page & trigger_page_bits &&
11291 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11293 change->actual_trigger_element = trigger_element;
11294 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11295 change->actual_trigger_player_bits = trigger_player;
11296 change->actual_trigger_side = trigger_side;
11297 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11298 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11301 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11302 element, EL_NAME(element), p);
11305 if ((change->can_change && !change_done) || change->has_action)
11309 SCAN_PLAYFIELD(x, y)
11311 if (Feld[x][y] == element)
11313 if (change->can_change && !change_done)
11315 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11316 /* if element already changed in this frame, not only prevent
11317 another element change (checked in ChangeElement()), but
11318 also prevent additional element actions for this element */
11320 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11321 !level.use_action_after_change_bug)
11326 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11327 element, EL_NAME(element), p);
11330 ChangeDelay[x][y] = 1;
11331 ChangeEvent[x][y] = trigger_event;
11333 HandleElementChange(x, y, p);
11335 #if USE_NEW_DELAYED_ACTION
11336 else if (change->has_action)
11338 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11339 /* if element already changed in this frame, not only prevent
11340 another element change (checked in ChangeElement()), but
11341 also prevent additional element actions for this element */
11343 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11344 !level.use_action_after_change_bug)
11350 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11351 element, EL_NAME(element), p);
11354 ExecuteCustomElementAction(x, y, element, p);
11355 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11358 if (change->has_action)
11360 ExecuteCustomElementAction(x, y, element, p);
11361 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11367 if (change->can_change)
11369 change_done = TRUE;
11370 change_done_any = TRUE;
11373 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11374 element, EL_NAME(element), p);
11383 RECURSION_LOOP_DETECTION_END();
11385 return change_done_any;
11388 static boolean CheckElementChangeExt(int x, int y,
11390 int trigger_element,
11392 int trigger_player,
11395 boolean change_done = FALSE;
11398 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11399 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11402 if (Feld[x][y] == EL_BLOCKED)
11404 Blocked2Moving(x, y, &x, &y);
11405 element = Feld[x][y];
11409 /* check if element has already changed */
11410 if (Feld[x][y] != element)
11413 /* check if element has already changed or is about to change after moving */
11414 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11415 Feld[x][y] != element) ||
11417 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11418 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11419 ChangePage[x][y] != -1)))
11424 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11425 trigger_event, recursion_loop_depth, recursion_loop_detected,
11426 recursion_loop_element, EL_NAME(recursion_loop_element));
11429 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11432 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11435 for (p = 0; p < element_info[element].num_change_pages; p++)
11437 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11439 /* check trigger element for all events where the element that is checked
11440 for changing interacts with a directly adjacent element -- this is
11441 different to element changes that affect other elements to change on the
11442 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11443 boolean check_trigger_element =
11444 (trigger_event == CE_TOUCHING_X ||
11445 trigger_event == CE_HITTING_X ||
11446 trigger_event == CE_HIT_BY_X ||
11448 /* this one was forgotten until 3.2.3 */
11449 trigger_event == CE_DIGGING_X);
11452 if (change->can_change_or_has_action &&
11453 change->has_event[trigger_event] &&
11454 change->trigger_side & trigger_side &&
11455 change->trigger_player & trigger_player &&
11456 (!check_trigger_element ||
11457 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11459 change->actual_trigger_element = trigger_element;
11460 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11461 change->actual_trigger_player_bits = trigger_player;
11462 change->actual_trigger_side = trigger_side;
11463 change->actual_trigger_ce_value = CustomValue[x][y];
11464 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11466 /* special case: trigger element not at (x,y) position for some events */
11467 if (check_trigger_element)
11479 { 0, 0 }, { 0, 0 }, { 0, 0 },
11483 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11484 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11486 change->actual_trigger_ce_value = CustomValue[xx][yy];
11487 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11490 if (change->can_change && !change_done)
11492 ChangeDelay[x][y] = 1;
11493 ChangeEvent[x][y] = trigger_event;
11495 HandleElementChange(x, y, p);
11497 change_done = TRUE;
11499 #if USE_NEW_DELAYED_ACTION
11500 else if (change->has_action)
11502 ExecuteCustomElementAction(x, y, element, p);
11503 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11506 if (change->has_action)
11508 ExecuteCustomElementAction(x, y, element, p);
11509 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11515 RECURSION_LOOP_DETECTION_END();
11517 return change_done;
11520 static void PlayPlayerSound(struct PlayerInfo *player)
11522 int jx = player->jx, jy = player->jy;
11523 int sound_element = player->artwork_element;
11524 int last_action = player->last_action_waiting;
11525 int action = player->action_waiting;
11527 if (player->is_waiting)
11529 if (action != last_action)
11530 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11532 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11536 if (action != last_action)
11537 StopSound(element_info[sound_element].sound[last_action]);
11539 if (last_action == ACTION_SLEEPING)
11540 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11544 static void PlayAllPlayersSound()
11548 for (i = 0; i < MAX_PLAYERS; i++)
11549 if (stored_player[i].active)
11550 PlayPlayerSound(&stored_player[i]);
11553 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11555 boolean last_waiting = player->is_waiting;
11556 int move_dir = player->MovDir;
11558 player->dir_waiting = move_dir;
11559 player->last_action_waiting = player->action_waiting;
11563 if (!last_waiting) /* not waiting -> waiting */
11565 player->is_waiting = TRUE;
11567 player->frame_counter_bored =
11569 game.player_boring_delay_fixed +
11570 GetSimpleRandom(game.player_boring_delay_random);
11571 player->frame_counter_sleeping =
11573 game.player_sleeping_delay_fixed +
11574 GetSimpleRandom(game.player_sleeping_delay_random);
11576 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11579 if (game.player_sleeping_delay_fixed +
11580 game.player_sleeping_delay_random > 0 &&
11581 player->anim_delay_counter == 0 &&
11582 player->post_delay_counter == 0 &&
11583 FrameCounter >= player->frame_counter_sleeping)
11584 player->is_sleeping = TRUE;
11585 else if (game.player_boring_delay_fixed +
11586 game.player_boring_delay_random > 0 &&
11587 FrameCounter >= player->frame_counter_bored)
11588 player->is_bored = TRUE;
11590 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11591 player->is_bored ? ACTION_BORING :
11594 if (player->is_sleeping && player->use_murphy)
11596 /* special case for sleeping Murphy when leaning against non-free tile */
11598 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11599 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11600 !IS_MOVING(player->jx - 1, player->jy)))
11601 move_dir = MV_LEFT;
11602 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11603 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11604 !IS_MOVING(player->jx + 1, player->jy)))
11605 move_dir = MV_RIGHT;
11607 player->is_sleeping = FALSE;
11609 player->dir_waiting = move_dir;
11612 if (player->is_sleeping)
11614 if (player->num_special_action_sleeping > 0)
11616 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11618 int last_special_action = player->special_action_sleeping;
11619 int num_special_action = player->num_special_action_sleeping;
11620 int special_action =
11621 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11622 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11623 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11624 last_special_action + 1 : ACTION_SLEEPING);
11625 int special_graphic =
11626 el_act_dir2img(player->artwork_element, special_action, move_dir);
11628 player->anim_delay_counter =
11629 graphic_info[special_graphic].anim_delay_fixed +
11630 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11631 player->post_delay_counter =
11632 graphic_info[special_graphic].post_delay_fixed +
11633 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11635 player->special_action_sleeping = special_action;
11638 if (player->anim_delay_counter > 0)
11640 player->action_waiting = player->special_action_sleeping;
11641 player->anim_delay_counter--;
11643 else if (player->post_delay_counter > 0)
11645 player->post_delay_counter--;
11649 else if (player->is_bored)
11651 if (player->num_special_action_bored > 0)
11653 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11655 int special_action =
11656 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11657 int special_graphic =
11658 el_act_dir2img(player->artwork_element, special_action, move_dir);
11660 player->anim_delay_counter =
11661 graphic_info[special_graphic].anim_delay_fixed +
11662 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11663 player->post_delay_counter =
11664 graphic_info[special_graphic].post_delay_fixed +
11665 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11667 player->special_action_bored = special_action;
11670 if (player->anim_delay_counter > 0)
11672 player->action_waiting = player->special_action_bored;
11673 player->anim_delay_counter--;
11675 else if (player->post_delay_counter > 0)
11677 player->post_delay_counter--;
11682 else if (last_waiting) /* waiting -> not waiting */
11684 player->is_waiting = FALSE;
11685 player->is_bored = FALSE;
11686 player->is_sleeping = FALSE;
11688 player->frame_counter_bored = -1;
11689 player->frame_counter_sleeping = -1;
11691 player->anim_delay_counter = 0;
11692 player->post_delay_counter = 0;
11694 player->dir_waiting = player->MovDir;
11695 player->action_waiting = ACTION_DEFAULT;
11697 player->special_action_bored = ACTION_DEFAULT;
11698 player->special_action_sleeping = ACTION_DEFAULT;
11702 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11704 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11705 int left = player_action & JOY_LEFT;
11706 int right = player_action & JOY_RIGHT;
11707 int up = player_action & JOY_UP;
11708 int down = player_action & JOY_DOWN;
11709 int button1 = player_action & JOY_BUTTON_1;
11710 int button2 = player_action & JOY_BUTTON_2;
11711 int dx = (left ? -1 : right ? 1 : 0);
11712 int dy = (up ? -1 : down ? 1 : 0);
11714 if (!player->active || tape.pausing)
11720 snapped = SnapField(player, dx, dy);
11724 dropped = DropElement(player);
11726 moved = MovePlayer(player, dx, dy);
11729 if (tape.single_step && tape.recording && !tape.pausing)
11731 if (button1 || (dropped && !moved))
11733 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11734 SnapField(player, 0, 0); /* stop snapping */
11738 SetPlayerWaiting(player, FALSE);
11740 return player_action;
11744 /* no actions for this player (no input at player's configured device) */
11746 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11747 SnapField(player, 0, 0);
11748 CheckGravityMovementWhenNotMoving(player);
11750 if (player->MovPos == 0)
11751 SetPlayerWaiting(player, TRUE);
11753 if (player->MovPos == 0) /* needed for tape.playing */
11754 player->is_moving = FALSE;
11756 player->is_dropping = FALSE;
11757 player->is_dropping_pressed = FALSE;
11758 player->drop_pressed_delay = 0;
11764 static void CheckLevelTime()
11768 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11770 if (level.native_em_level->lev->home == 0) /* all players at home */
11772 PlayerWins(local_player);
11774 AllPlayersGone = TRUE;
11776 level.native_em_level->lev->home = -1;
11779 if (level.native_em_level->ply[0]->alive == 0 &&
11780 level.native_em_level->ply[1]->alive == 0 &&
11781 level.native_em_level->ply[2]->alive == 0 &&
11782 level.native_em_level->ply[3]->alive == 0) /* all dead */
11783 AllPlayersGone = TRUE;
11786 if (TimeFrames >= FRAMES_PER_SECOND)
11791 for (i = 0; i < MAX_PLAYERS; i++)
11793 struct PlayerInfo *player = &stored_player[i];
11795 if (SHIELD_ON(player))
11797 player->shield_normal_time_left--;
11799 if (player->shield_deadly_time_left > 0)
11800 player->shield_deadly_time_left--;
11804 if (!local_player->LevelSolved && !level.use_step_counter)
11812 if (TimeLeft <= 10 && setup.time_limit)
11813 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11816 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11818 DisplayGameControlValues();
11820 DrawGameValue_Time(TimeLeft);
11823 if (!TimeLeft && setup.time_limit)
11825 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11826 level.native_em_level->lev->killed_out_of_time = TRUE;
11828 for (i = 0; i < MAX_PLAYERS; i++)
11829 KillPlayer(&stored_player[i]);
11833 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11835 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11837 DisplayGameControlValues();
11840 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11841 DrawGameValue_Time(TimePlayed);
11844 level.native_em_level->lev->time =
11845 (level.time == 0 ? TimePlayed : TimeLeft);
11848 if (tape.recording || tape.playing)
11849 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11853 UpdateAndDisplayGameControlValues();
11855 UpdateGameDoorValues();
11856 DrawGameDoorValues();
11860 void AdvanceFrameAndPlayerCounters(int player_nr)
11864 /* advance frame counters (global frame counter and time frame counter) */
11868 /* advance player counters (counters for move delay, move animation etc.) */
11869 for (i = 0; i < MAX_PLAYERS; i++)
11871 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11872 int move_delay_value = stored_player[i].move_delay_value;
11873 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11875 if (!advance_player_counters) /* not all players may be affected */
11878 #if USE_NEW_PLAYER_ANIM
11879 if (move_frames == 0) /* less than one move per game frame */
11881 int stepsize = TILEX / move_delay_value;
11882 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11883 int count = (stored_player[i].is_moving ?
11884 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11886 if (count % delay == 0)
11891 stored_player[i].Frame += move_frames;
11893 if (stored_player[i].MovPos != 0)
11894 stored_player[i].StepFrame += move_frames;
11896 if (stored_player[i].move_delay > 0)
11897 stored_player[i].move_delay--;
11899 /* due to bugs in previous versions, counter must count up, not down */
11900 if (stored_player[i].push_delay != -1)
11901 stored_player[i].push_delay++;
11903 if (stored_player[i].drop_delay > 0)
11904 stored_player[i].drop_delay--;
11906 if (stored_player[i].is_dropping_pressed)
11907 stored_player[i].drop_pressed_delay++;
11911 void StartGameActions(boolean init_network_game, boolean record_tape,
11914 unsigned long new_random_seed = InitRND(random_seed);
11917 TapeStartRecording(new_random_seed);
11919 #if defined(NETWORK_AVALIABLE)
11920 if (init_network_game)
11922 SendToServer_StartPlaying();
11933 static unsigned long game_frame_delay = 0;
11934 unsigned long game_frame_delay_value;
11935 byte *recorded_player_action;
11936 byte summarized_player_action = 0;
11937 byte tape_action[MAX_PLAYERS];
11940 /* detect endless loops, caused by custom element programming */
11941 if (recursion_loop_detected && recursion_loop_depth == 0)
11943 char *message = getStringCat3("Internal Error ! Element ",
11944 EL_NAME(recursion_loop_element),
11945 " caused endless loop ! Quit the game ?");
11947 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11948 EL_NAME(recursion_loop_element));
11950 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11952 recursion_loop_detected = FALSE; /* if game should be continued */
11959 if (game.restart_level)
11960 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11962 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11964 if (level.native_em_level->lev->home == 0) /* all players at home */
11966 PlayerWins(local_player);
11968 AllPlayersGone = TRUE;
11970 level.native_em_level->lev->home = -1;
11973 if (level.native_em_level->ply[0]->alive == 0 &&
11974 level.native_em_level->ply[1]->alive == 0 &&
11975 level.native_em_level->ply[2]->alive == 0 &&
11976 level.native_em_level->ply[3]->alive == 0) /* all dead */
11977 AllPlayersGone = TRUE;
11980 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11983 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11986 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11989 game_frame_delay_value =
11990 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11992 if (tape.playing && tape.warp_forward && !tape.pausing)
11993 game_frame_delay_value = 0;
11995 /* ---------- main game synchronization point ---------- */
11997 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11999 if (network_playing && !network_player_action_received)
12001 /* try to get network player actions in time */
12003 #if defined(NETWORK_AVALIABLE)
12004 /* last chance to get network player actions without main loop delay */
12005 HandleNetworking();
12008 /* game was quit by network peer */
12009 if (game_status != GAME_MODE_PLAYING)
12012 if (!network_player_action_received)
12013 return; /* failed to get network player actions in time */
12015 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12021 /* at this point we know that we really continue executing the game */
12023 network_player_action_received = FALSE;
12025 /* when playing tape, read previously recorded player input from tape data */
12026 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12029 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12034 if (tape.set_centered_player)
12036 game.centered_player_nr_next = tape.centered_player_nr_next;
12037 game.set_centered_player = TRUE;
12040 for (i = 0; i < MAX_PLAYERS; i++)
12042 summarized_player_action |= stored_player[i].action;
12044 if (!network_playing)
12045 stored_player[i].effective_action = stored_player[i].action;
12048 #if defined(NETWORK_AVALIABLE)
12049 if (network_playing)
12050 SendToServer_MovePlayer(summarized_player_action);
12053 if (!options.network && !setup.team_mode)
12054 local_player->effective_action = summarized_player_action;
12056 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12058 for (i = 0; i < MAX_PLAYERS; i++)
12059 stored_player[i].effective_action =
12060 (i == game.centered_player_nr ? summarized_player_action : 0);
12063 if (recorded_player_action != NULL)
12064 for (i = 0; i < MAX_PLAYERS; i++)
12065 stored_player[i].effective_action = recorded_player_action[i];
12067 for (i = 0; i < MAX_PLAYERS; i++)
12069 tape_action[i] = stored_player[i].effective_action;
12071 /* (this can only happen in the R'n'D game engine) */
12072 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12073 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12076 /* only record actions from input devices, but not programmed actions */
12077 if (tape.recording)
12078 TapeRecordAction(tape_action);
12080 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12082 GameActions_EM_Main();
12090 void GameActions_EM_Main()
12092 byte effective_action[MAX_PLAYERS];
12093 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12096 for (i = 0; i < MAX_PLAYERS; i++)
12097 effective_action[i] = stored_player[i].effective_action;
12099 GameActions_EM(effective_action, warp_mode);
12103 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12106 void GameActions_RND()
12108 int magic_wall_x = 0, magic_wall_y = 0;
12109 int i, x, y, element, graphic;
12111 InitPlayfieldScanModeVars();
12113 #if USE_ONE_MORE_CHANGE_PER_FRAME
12114 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12116 SCAN_PLAYFIELD(x, y)
12118 ChangeCount[x][y] = 0;
12119 ChangeEvent[x][y] = -1;
12124 if (game.set_centered_player)
12126 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12128 /* switching to "all players" only possible if all players fit to screen */
12129 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12131 game.centered_player_nr_next = game.centered_player_nr;
12132 game.set_centered_player = FALSE;
12135 /* do not switch focus to non-existing (or non-active) player */
12136 if (game.centered_player_nr_next >= 0 &&
12137 !stored_player[game.centered_player_nr_next].active)
12139 game.centered_player_nr_next = game.centered_player_nr;
12140 game.set_centered_player = FALSE;
12144 if (game.set_centered_player &&
12145 ScreenMovPos == 0) /* screen currently aligned at tile position */
12149 if (game.centered_player_nr_next == -1)
12151 setScreenCenteredToAllPlayers(&sx, &sy);
12155 sx = stored_player[game.centered_player_nr_next].jx;
12156 sy = stored_player[game.centered_player_nr_next].jy;
12159 game.centered_player_nr = game.centered_player_nr_next;
12160 game.set_centered_player = FALSE;
12162 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12163 DrawGameDoorValues();
12166 for (i = 0; i < MAX_PLAYERS; i++)
12168 int actual_player_action = stored_player[i].effective_action;
12171 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12172 - rnd_equinox_tetrachloride 048
12173 - rnd_equinox_tetrachloride_ii 096
12174 - rnd_emanuel_schmieg 002
12175 - doctor_sloan_ww 001, 020
12177 if (stored_player[i].MovPos == 0)
12178 CheckGravityMovement(&stored_player[i]);
12181 /* overwrite programmed action with tape action */
12182 if (stored_player[i].programmed_action)
12183 actual_player_action = stored_player[i].programmed_action;
12185 PlayerActions(&stored_player[i], actual_player_action);
12187 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12190 ScrollScreen(NULL, SCROLL_GO_ON);
12192 /* for backwards compatibility, the following code emulates a fixed bug that
12193 occured when pushing elements (causing elements that just made their last
12194 pushing step to already (if possible) make their first falling step in the
12195 same game frame, which is bad); this code is also needed to use the famous
12196 "spring push bug" which is used in older levels and might be wanted to be
12197 used also in newer levels, but in this case the buggy pushing code is only
12198 affecting the "spring" element and no other elements */
12200 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12202 for (i = 0; i < MAX_PLAYERS; i++)
12204 struct PlayerInfo *player = &stored_player[i];
12205 int x = player->jx;
12206 int y = player->jy;
12208 if (player->active && player->is_pushing && player->is_moving &&
12210 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12211 Feld[x][y] == EL_SPRING))
12213 ContinueMoving(x, y);
12215 /* continue moving after pushing (this is actually a bug) */
12216 if (!IS_MOVING(x, y))
12217 Stop[x][y] = FALSE;
12223 debug_print_timestamp(0, "start main loop profiling");
12226 SCAN_PLAYFIELD(x, y)
12228 ChangeCount[x][y] = 0;
12229 ChangeEvent[x][y] = -1;
12231 /* this must be handled before main playfield loop */
12232 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12235 if (MovDelay[x][y] <= 0)
12239 #if USE_NEW_SNAP_DELAY
12240 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12243 if (MovDelay[x][y] <= 0)
12246 TEST_DrawLevelField(x, y);
12248 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12254 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12256 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12257 printf("GameActions(): This should never happen!\n");
12259 ChangePage[x][y] = -1;
12263 Stop[x][y] = FALSE;
12264 if (WasJustMoving[x][y] > 0)
12265 WasJustMoving[x][y]--;
12266 if (WasJustFalling[x][y] > 0)
12267 WasJustFalling[x][y]--;
12268 if (CheckCollision[x][y] > 0)
12269 CheckCollision[x][y]--;
12270 if (CheckImpact[x][y] > 0)
12271 CheckImpact[x][y]--;
12275 /* reset finished pushing action (not done in ContinueMoving() to allow
12276 continuous pushing animation for elements with zero push delay) */
12277 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12279 ResetGfxAnimation(x, y);
12280 TEST_DrawLevelField(x, y);
12284 if (IS_BLOCKED(x, y))
12288 Blocked2Moving(x, y, &oldx, &oldy);
12289 if (!IS_MOVING(oldx, oldy))
12291 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12292 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12293 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12294 printf("GameActions(): This should never happen!\n");
12301 debug_print_timestamp(0, "- time for pre-main loop:");
12304 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12305 SCAN_PLAYFIELD(x, y)
12307 element = Feld[x][y];
12308 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12313 int element2 = element;
12314 int graphic2 = graphic;
12316 int element2 = Feld[x][y];
12317 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12319 int last_gfx_frame = GfxFrame[x][y];
12321 if (graphic_info[graphic2].anim_global_sync)
12322 GfxFrame[x][y] = FrameCounter;
12323 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12324 GfxFrame[x][y] = CustomValue[x][y];
12325 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12326 GfxFrame[x][y] = element_info[element2].collect_score;
12327 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12328 GfxFrame[x][y] = ChangeDelay[x][y];
12330 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12331 DrawLevelGraphicAnimation(x, y, graphic2);
12334 ResetGfxFrame(x, y, TRUE);
12338 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12339 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12340 ResetRandomAnimationValue(x, y);
12344 SetRandomAnimationValue(x, y);
12348 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12351 #endif // -------------------- !!! TEST ONLY !!! --------------------
12354 debug_print_timestamp(0, "- time for TEST loop: -->");
12357 SCAN_PLAYFIELD(x, y)
12359 element = Feld[x][y];
12360 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12362 ResetGfxFrame(x, y, TRUE);
12364 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12365 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12366 ResetRandomAnimationValue(x, y);
12368 SetRandomAnimationValue(x, y);
12370 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12372 if (IS_INACTIVE(element))
12374 if (IS_ANIMATED(graphic))
12375 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12380 /* this may take place after moving, so 'element' may have changed */
12381 if (IS_CHANGING(x, y) &&
12382 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12384 int page = element_info[element].event_page_nr[CE_DELAY];
12387 HandleElementChange(x, y, page);
12389 if (CAN_CHANGE(element))
12390 HandleElementChange(x, y, page);
12392 if (HAS_ACTION(element))
12393 ExecuteCustomElementAction(x, y, element, page);
12396 element = Feld[x][y];
12397 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12400 #if 0 // ---------------------------------------------------------------------
12402 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12406 element = Feld[x][y];
12407 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12409 if (IS_ANIMATED(graphic) &&
12410 !IS_MOVING(x, y) &&
12412 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12414 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12415 TEST_DrawTwinkleOnField(x, y);
12417 else if (IS_MOVING(x, y))
12418 ContinueMoving(x, y);
12425 case EL_EM_EXIT_OPEN:
12426 case EL_SP_EXIT_OPEN:
12427 case EL_STEEL_EXIT_OPEN:
12428 case EL_EM_STEEL_EXIT_OPEN:
12429 case EL_SP_TERMINAL:
12430 case EL_SP_TERMINAL_ACTIVE:
12431 case EL_EXTRA_TIME:
12432 case EL_SHIELD_NORMAL:
12433 case EL_SHIELD_DEADLY:
12434 if (IS_ANIMATED(graphic))
12435 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12438 case EL_DYNAMITE_ACTIVE:
12439 case EL_EM_DYNAMITE_ACTIVE:
12440 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12441 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12442 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12443 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12444 case EL_SP_DISK_RED_ACTIVE:
12445 CheckDynamite(x, y);
12448 case EL_AMOEBA_GROWING:
12449 AmoebeWaechst(x, y);
12452 case EL_AMOEBA_SHRINKING:
12453 AmoebaDisappearing(x, y);
12456 #if !USE_NEW_AMOEBA_CODE
12457 case EL_AMOEBA_WET:
12458 case EL_AMOEBA_DRY:
12459 case EL_AMOEBA_FULL:
12461 case EL_EMC_DRIPPER:
12462 AmoebeAbleger(x, y);
12466 case EL_GAME_OF_LIFE:
12471 case EL_EXIT_CLOSED:
12475 case EL_EM_EXIT_CLOSED:
12479 case EL_STEEL_EXIT_CLOSED:
12480 CheckExitSteel(x, y);
12483 case EL_EM_STEEL_EXIT_CLOSED:
12484 CheckExitSteelEM(x, y);
12487 case EL_SP_EXIT_CLOSED:
12491 case EL_EXPANDABLE_WALL_GROWING:
12492 case EL_EXPANDABLE_STEELWALL_GROWING:
12493 MauerWaechst(x, y);
12496 case EL_EXPANDABLE_WALL:
12497 case EL_EXPANDABLE_WALL_HORIZONTAL:
12498 case EL_EXPANDABLE_WALL_VERTICAL:
12499 case EL_EXPANDABLE_WALL_ANY:
12500 case EL_BD_EXPANDABLE_WALL:
12501 MauerAbleger(x, y);
12504 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12505 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12506 case EL_EXPANDABLE_STEELWALL_ANY:
12507 MauerAblegerStahl(x, y);
12511 CheckForDragon(x, y);
12517 case EL_ELEMENT_SNAPPING:
12518 case EL_DIAGONAL_SHRINKING:
12519 case EL_DIAGONAL_GROWING:
12522 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12524 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12529 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12530 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12535 #else // ---------------------------------------------------------------------
12537 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12541 element = Feld[x][y];
12542 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12544 if (IS_ANIMATED(graphic) &&
12545 !IS_MOVING(x, y) &&
12547 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12549 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12550 TEST_DrawTwinkleOnField(x, y);
12552 else if ((element == EL_ACID ||
12553 element == EL_EXIT_OPEN ||
12554 element == EL_EM_EXIT_OPEN ||
12555 element == EL_SP_EXIT_OPEN ||
12556 element == EL_STEEL_EXIT_OPEN ||
12557 element == EL_EM_STEEL_EXIT_OPEN ||
12558 element == EL_SP_TERMINAL ||
12559 element == EL_SP_TERMINAL_ACTIVE ||
12560 element == EL_EXTRA_TIME ||
12561 element == EL_SHIELD_NORMAL ||
12562 element == EL_SHIELD_DEADLY) &&
12563 IS_ANIMATED(graphic))
12564 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12565 else if (IS_MOVING(x, y))
12566 ContinueMoving(x, y);
12567 else if (IS_ACTIVE_BOMB(element))
12568 CheckDynamite(x, y);
12569 else if (element == EL_AMOEBA_GROWING)
12570 AmoebeWaechst(x, y);
12571 else if (element == EL_AMOEBA_SHRINKING)
12572 AmoebaDisappearing(x, y);
12574 #if !USE_NEW_AMOEBA_CODE
12575 else if (IS_AMOEBALIVE(element))
12576 AmoebeAbleger(x, y);
12579 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12581 else if (element == EL_EXIT_CLOSED)
12583 else if (element == EL_EM_EXIT_CLOSED)
12585 else if (element == EL_STEEL_EXIT_CLOSED)
12586 CheckExitSteel(x, y);
12587 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12588 CheckExitSteelEM(x, y);
12589 else if (element == EL_SP_EXIT_CLOSED)
12591 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12592 element == EL_EXPANDABLE_STEELWALL_GROWING)
12593 MauerWaechst(x, y);
12594 else if (element == EL_EXPANDABLE_WALL ||
12595 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12596 element == EL_EXPANDABLE_WALL_VERTICAL ||
12597 element == EL_EXPANDABLE_WALL_ANY ||
12598 element == EL_BD_EXPANDABLE_WALL)
12599 MauerAbleger(x, y);
12600 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12601 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12602 element == EL_EXPANDABLE_STEELWALL_ANY)
12603 MauerAblegerStahl(x, y);
12604 else if (element == EL_FLAMES)
12605 CheckForDragon(x, y);
12606 else if (element == EL_EXPLOSION)
12607 ; /* drawing of correct explosion animation is handled separately */
12608 else if (element == EL_ELEMENT_SNAPPING ||
12609 element == EL_DIAGONAL_SHRINKING ||
12610 element == EL_DIAGONAL_GROWING)
12612 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12614 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12616 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12619 #endif // ---------------------------------------------------------------------
12621 if (IS_BELT_ACTIVE(element))
12622 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12624 if (game.magic_wall_active)
12626 int jx = local_player->jx, jy = local_player->jy;
12628 /* play the element sound at the position nearest to the player */
12629 if ((element == EL_MAGIC_WALL_FULL ||
12630 element == EL_MAGIC_WALL_ACTIVE ||
12631 element == EL_MAGIC_WALL_EMPTYING ||
12632 element == EL_BD_MAGIC_WALL_FULL ||
12633 element == EL_BD_MAGIC_WALL_ACTIVE ||
12634 element == EL_BD_MAGIC_WALL_EMPTYING ||
12635 element == EL_DC_MAGIC_WALL_FULL ||
12636 element == EL_DC_MAGIC_WALL_ACTIVE ||
12637 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12638 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12647 debug_print_timestamp(0, "- time for MAIN loop: -->");
12650 #if USE_NEW_AMOEBA_CODE
12651 /* new experimental amoeba growth stuff */
12652 if (!(FrameCounter % 8))
12654 static unsigned long random = 1684108901;
12656 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12658 x = RND(lev_fieldx);
12659 y = RND(lev_fieldy);
12660 element = Feld[x][y];
12662 if (!IS_PLAYER(x,y) &&
12663 (element == EL_EMPTY ||
12664 CAN_GROW_INTO(element) ||
12665 element == EL_QUICKSAND_EMPTY ||
12666 element == EL_QUICKSAND_FAST_EMPTY ||
12667 element == EL_ACID_SPLASH_LEFT ||
12668 element == EL_ACID_SPLASH_RIGHT))
12670 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12671 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12672 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12673 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12674 Feld[x][y] = EL_AMOEBA_DROP;
12677 random = random * 129 + 1;
12683 if (game.explosions_delayed)
12686 game.explosions_delayed = FALSE;
12688 SCAN_PLAYFIELD(x, y)
12690 element = Feld[x][y];
12692 if (ExplodeField[x][y])
12693 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12694 else if (element == EL_EXPLOSION)
12695 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12697 ExplodeField[x][y] = EX_TYPE_NONE;
12700 game.explosions_delayed = TRUE;
12703 if (game.magic_wall_active)
12705 if (!(game.magic_wall_time_left % 4))
12707 int element = Feld[magic_wall_x][magic_wall_y];
12709 if (element == EL_BD_MAGIC_WALL_FULL ||
12710 element == EL_BD_MAGIC_WALL_ACTIVE ||
12711 element == EL_BD_MAGIC_WALL_EMPTYING)
12712 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12713 else if (element == EL_DC_MAGIC_WALL_FULL ||
12714 element == EL_DC_MAGIC_WALL_ACTIVE ||
12715 element == EL_DC_MAGIC_WALL_EMPTYING)
12716 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12718 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12721 if (game.magic_wall_time_left > 0)
12723 game.magic_wall_time_left--;
12725 if (!game.magic_wall_time_left)
12727 SCAN_PLAYFIELD(x, y)
12729 element = Feld[x][y];
12731 if (element == EL_MAGIC_WALL_ACTIVE ||
12732 element == EL_MAGIC_WALL_FULL)
12734 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12735 TEST_DrawLevelField(x, y);
12737 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12738 element == EL_BD_MAGIC_WALL_FULL)
12740 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12741 TEST_DrawLevelField(x, y);
12743 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12744 element == EL_DC_MAGIC_WALL_FULL)
12746 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12747 TEST_DrawLevelField(x, y);
12751 game.magic_wall_active = FALSE;
12756 if (game.light_time_left > 0)
12758 game.light_time_left--;
12760 if (game.light_time_left == 0)
12761 RedrawAllLightSwitchesAndInvisibleElements();
12764 if (game.timegate_time_left > 0)
12766 game.timegate_time_left--;
12768 if (game.timegate_time_left == 0)
12769 CloseAllOpenTimegates();
12772 if (game.lenses_time_left > 0)
12774 game.lenses_time_left--;
12776 if (game.lenses_time_left == 0)
12777 RedrawAllInvisibleElementsForLenses();
12780 if (game.magnify_time_left > 0)
12782 game.magnify_time_left--;
12784 if (game.magnify_time_left == 0)
12785 RedrawAllInvisibleElementsForMagnifier();
12788 for (i = 0; i < MAX_PLAYERS; i++)
12790 struct PlayerInfo *player = &stored_player[i];
12792 if (SHIELD_ON(player))
12794 if (player->shield_deadly_time_left)
12795 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12796 else if (player->shield_normal_time_left)
12797 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12801 #if USE_DELAYED_GFX_REDRAW
12802 SCAN_PLAYFIELD(x, y)
12805 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12807 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12808 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12811 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12812 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12814 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12815 DrawLevelField(x, y);
12817 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12818 DrawLevelFieldCrumbledSand(x, y);
12820 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12821 DrawLevelFieldCrumbledSandNeighbours(x, y);
12823 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12824 DrawTwinkleOnField(x, y);
12827 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12834 PlayAllPlayersSound();
12836 if (options.debug) /* calculate frames per second */
12838 static unsigned long fps_counter = 0;
12839 static int fps_frames = 0;
12840 unsigned long fps_delay_ms = Counter() - fps_counter;
12844 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12846 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12849 fps_counter = Counter();
12852 redraw_mask |= REDRAW_FPS;
12855 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12857 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12859 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12861 local_player->show_envelope = 0;
12865 debug_print_timestamp(0, "stop main loop profiling ");
12866 printf("----------------------------------------------------------\n");
12869 /* use random number generator in every frame to make it less predictable */
12870 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12874 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12876 int min_x = x, min_y = y, max_x = x, max_y = y;
12879 for (i = 0; i < MAX_PLAYERS; i++)
12881 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12883 if (!stored_player[i].active || &stored_player[i] == player)
12886 min_x = MIN(min_x, jx);
12887 min_y = MIN(min_y, jy);
12888 max_x = MAX(max_x, jx);
12889 max_y = MAX(max_y, jy);
12892 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12895 static boolean AllPlayersInVisibleScreen()
12899 for (i = 0; i < MAX_PLAYERS; i++)
12901 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12903 if (!stored_player[i].active)
12906 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12913 void ScrollLevel(int dx, int dy)
12916 /* (directly solved in BlitBitmap() now) */
12917 static Bitmap *bitmap_db_field2 = NULL;
12918 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12925 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12926 /* only horizontal XOR vertical scroll direction allowed */
12927 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12932 /* (directly solved in BlitBitmap() now) */
12933 if (bitmap_db_field2 == NULL)
12934 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12936 /* needed when blitting directly to same bitmap -- should not be needed with
12937 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12938 BlitBitmap(drawto_field, bitmap_db_field2,
12939 FX + TILEX * (dx == -1) - softscroll_offset,
12940 FY + TILEY * (dy == -1) - softscroll_offset,
12941 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12942 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12943 FX + TILEX * (dx == 1) - softscroll_offset,
12944 FY + TILEY * (dy == 1) - softscroll_offset);
12945 BlitBitmap(bitmap_db_field2, drawto_field,
12946 FX + TILEX * (dx == 1) - softscroll_offset,
12947 FY + TILEY * (dy == 1) - softscroll_offset,
12948 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12949 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12950 FX + TILEX * (dx == 1) - softscroll_offset,
12951 FY + TILEY * (dy == 1) - softscroll_offset);
12956 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12957 int xsize = (BX2 - BX1 + 1);
12958 int ysize = (BY2 - BY1 + 1);
12959 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12960 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12961 int step = (start < end ? +1 : -1);
12963 for (i = start; i != end; i += step)
12965 BlitBitmap(drawto_field, drawto_field,
12966 FX + TILEX * (dx != 0 ? i + step : 0),
12967 FY + TILEY * (dy != 0 ? i + step : 0),
12968 TILEX * (dx != 0 ? 1 : xsize),
12969 TILEY * (dy != 0 ? 1 : ysize),
12970 FX + TILEX * (dx != 0 ? i : 0),
12971 FY + TILEY * (dy != 0 ? i : 0));
12976 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12978 BlitBitmap(drawto_field, drawto_field,
12979 FX + TILEX * (dx == -1) - softscroll_offset,
12980 FY + TILEY * (dy == -1) - softscroll_offset,
12981 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12982 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12983 FX + TILEX * (dx == 1) - softscroll_offset,
12984 FY + TILEY * (dy == 1) - softscroll_offset);
12990 x = (dx == 1 ? BX1 : BX2);
12991 for (y = BY1; y <= BY2; y++)
12992 DrawScreenField(x, y);
12997 y = (dy == 1 ? BY1 : BY2);
12998 for (x = BX1; x <= BX2; x++)
12999 DrawScreenField(x, y);
13002 redraw_mask |= REDRAW_FIELD;
13005 static boolean canFallDown(struct PlayerInfo *player)
13007 int jx = player->jx, jy = player->jy;
13009 return (IN_LEV_FIELD(jx, jy + 1) &&
13010 (IS_FREE(jx, jy + 1) ||
13011 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13012 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13013 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13016 static boolean canPassField(int x, int y, int move_dir)
13018 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13019 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13020 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13021 int nextx = x + dx;
13022 int nexty = y + dy;
13023 int element = Feld[x][y];
13025 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13026 !CAN_MOVE(element) &&
13027 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13028 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13029 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13032 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13034 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13035 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13036 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13040 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13041 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13042 (IS_DIGGABLE(Feld[newx][newy]) ||
13043 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13044 canPassField(newx, newy, move_dir)));
13047 static void CheckGravityMovement(struct PlayerInfo *player)
13049 #if USE_PLAYER_GRAVITY
13050 if (player->gravity && !player->programmed_action)
13052 if (game.gravity && !player->programmed_action)
13055 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13056 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13057 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13058 int jx = player->jx, jy = player->jy;
13059 boolean player_is_moving_to_valid_field =
13060 (!player_is_snapping &&
13061 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13062 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13063 boolean player_can_fall_down = canFallDown(player);
13065 if (player_can_fall_down &&
13066 !player_is_moving_to_valid_field)
13067 player->programmed_action = MV_DOWN;
13071 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13073 return CheckGravityMovement(player);
13075 #if USE_PLAYER_GRAVITY
13076 if (player->gravity && !player->programmed_action)
13078 if (game.gravity && !player->programmed_action)
13081 int jx = player->jx, jy = player->jy;
13082 boolean field_under_player_is_free =
13083 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13084 boolean player_is_standing_on_valid_field =
13085 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13086 (IS_WALKABLE(Feld[jx][jy]) &&
13087 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13089 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13090 player->programmed_action = MV_DOWN;
13095 MovePlayerOneStep()
13096 -----------------------------------------------------------------------------
13097 dx, dy: direction (non-diagonal) to try to move the player to
13098 real_dx, real_dy: direction as read from input device (can be diagonal)
13101 boolean MovePlayerOneStep(struct PlayerInfo *player,
13102 int dx, int dy, int real_dx, int real_dy)
13104 int jx = player->jx, jy = player->jy;
13105 int new_jx = jx + dx, new_jy = jy + dy;
13106 #if !USE_FIXED_DONT_RUN_INTO
13110 boolean player_can_move = !player->cannot_move;
13112 if (!player->active || (!dx && !dy))
13113 return MP_NO_ACTION;
13115 player->MovDir = (dx < 0 ? MV_LEFT :
13116 dx > 0 ? MV_RIGHT :
13118 dy > 0 ? MV_DOWN : MV_NONE);
13120 if (!IN_LEV_FIELD(new_jx, new_jy))
13121 return MP_NO_ACTION;
13123 if (!player_can_move)
13125 if (player->MovPos == 0)
13127 player->is_moving = FALSE;
13128 player->is_digging = FALSE;
13129 player->is_collecting = FALSE;
13130 player->is_snapping = FALSE;
13131 player->is_pushing = FALSE;
13136 if (!options.network && game.centered_player_nr == -1 &&
13137 !AllPlayersInSight(player, new_jx, new_jy))
13138 return MP_NO_ACTION;
13140 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13141 return MP_NO_ACTION;
13144 #if !USE_FIXED_DONT_RUN_INTO
13145 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13147 /* (moved to DigField()) */
13148 if (player_can_move && DONT_RUN_INTO(element))
13150 if (element == EL_ACID && dx == 0 && dy == 1)
13152 SplashAcid(new_jx, new_jy);
13153 Feld[jx][jy] = EL_PLAYER_1;
13154 InitMovingField(jx, jy, MV_DOWN);
13155 Store[jx][jy] = EL_ACID;
13156 ContinueMoving(jx, jy);
13157 BuryPlayer(player);
13160 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13166 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13167 if (can_move != MP_MOVING)
13170 /* check if DigField() has caused relocation of the player */
13171 if (player->jx != jx || player->jy != jy)
13172 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13174 StorePlayer[jx][jy] = 0;
13175 player->last_jx = jx;
13176 player->last_jy = jy;
13177 player->jx = new_jx;
13178 player->jy = new_jy;
13179 StorePlayer[new_jx][new_jy] = player->element_nr;
13181 if (player->move_delay_value_next != -1)
13183 player->move_delay_value = player->move_delay_value_next;
13184 player->move_delay_value_next = -1;
13188 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13190 player->step_counter++;
13192 PlayerVisit[jx][jy] = FrameCounter;
13194 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13195 player->is_moving = TRUE;
13199 /* should better be called in MovePlayer(), but this breaks some tapes */
13200 ScrollPlayer(player, SCROLL_INIT);
13206 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13208 int jx = player->jx, jy = player->jy;
13209 int old_jx = jx, old_jy = jy;
13210 int moved = MP_NO_ACTION;
13212 if (!player->active)
13217 if (player->MovPos == 0)
13219 player->is_moving = FALSE;
13220 player->is_digging = FALSE;
13221 player->is_collecting = FALSE;
13222 player->is_snapping = FALSE;
13223 player->is_pushing = FALSE;
13229 if (player->move_delay > 0)
13232 player->move_delay = -1; /* set to "uninitialized" value */
13234 /* store if player is automatically moved to next field */
13235 player->is_auto_moving = (player->programmed_action != MV_NONE);
13237 /* remove the last programmed player action */
13238 player->programmed_action = 0;
13240 if (player->MovPos)
13242 /* should only happen if pre-1.2 tape recordings are played */
13243 /* this is only for backward compatibility */
13245 int original_move_delay_value = player->move_delay_value;
13248 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13252 /* scroll remaining steps with finest movement resolution */
13253 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13255 while (player->MovPos)
13257 ScrollPlayer(player, SCROLL_GO_ON);
13258 ScrollScreen(NULL, SCROLL_GO_ON);
13260 AdvanceFrameAndPlayerCounters(player->index_nr);
13266 player->move_delay_value = original_move_delay_value;
13269 player->is_active = FALSE;
13271 if (player->last_move_dir & MV_HORIZONTAL)
13273 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13274 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13278 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13279 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13282 #if USE_FIXED_BORDER_RUNNING_GFX
13283 if (!moved && !player->is_active)
13285 player->is_moving = FALSE;
13286 player->is_digging = FALSE;
13287 player->is_collecting = FALSE;
13288 player->is_snapping = FALSE;
13289 player->is_pushing = FALSE;
13297 if (moved & MP_MOVING && !ScreenMovPos &&
13298 (player->index_nr == game.centered_player_nr ||
13299 game.centered_player_nr == -1))
13301 if (moved & MP_MOVING && !ScreenMovPos &&
13302 (player == local_player || !options.network))
13305 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13306 int offset = game.scroll_delay_value;
13308 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13310 /* actual player has left the screen -- scroll in that direction */
13311 if (jx != old_jx) /* player has moved horizontally */
13312 scroll_x += (jx - old_jx);
13313 else /* player has moved vertically */
13314 scroll_y += (jy - old_jy);
13318 if (jx != old_jx) /* player has moved horizontally */
13320 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13321 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13322 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13324 /* don't scroll over playfield boundaries */
13325 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13326 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13328 /* don't scroll more than one field at a time */
13329 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13331 /* don't scroll against the player's moving direction */
13332 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13333 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13334 scroll_x = old_scroll_x;
13336 else /* player has moved vertically */
13338 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13339 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13340 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13342 /* don't scroll over playfield boundaries */
13343 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13344 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13346 /* don't scroll more than one field at a time */
13347 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13349 /* don't scroll against the player's moving direction */
13350 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13351 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13352 scroll_y = old_scroll_y;
13356 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13359 if (!options.network && game.centered_player_nr == -1 &&
13360 !AllPlayersInVisibleScreen())
13362 scroll_x = old_scroll_x;
13363 scroll_y = old_scroll_y;
13367 if (!options.network && !AllPlayersInVisibleScreen())
13369 scroll_x = old_scroll_x;
13370 scroll_y = old_scroll_y;
13375 ScrollScreen(player, SCROLL_INIT);
13376 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13381 player->StepFrame = 0;
13383 if (moved & MP_MOVING)
13385 if (old_jx != jx && old_jy == jy)
13386 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13387 else if (old_jx == jx && old_jy != jy)
13388 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13390 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13392 player->last_move_dir = player->MovDir;
13393 player->is_moving = TRUE;
13394 player->is_snapping = FALSE;
13395 player->is_switching = FALSE;
13396 player->is_dropping = FALSE;
13397 player->is_dropping_pressed = FALSE;
13398 player->drop_pressed_delay = 0;
13401 /* should better be called here than above, but this breaks some tapes */
13402 ScrollPlayer(player, SCROLL_INIT);
13407 CheckGravityMovementWhenNotMoving(player);
13409 player->is_moving = FALSE;
13411 /* at this point, the player is allowed to move, but cannot move right now
13412 (e.g. because of something blocking the way) -- ensure that the player
13413 is also allowed to move in the next frame (in old versions before 3.1.1,
13414 the player was forced to wait again for eight frames before next try) */
13416 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13417 player->move_delay = 0; /* allow direct movement in the next frame */
13420 if (player->move_delay == -1) /* not yet initialized by DigField() */
13421 player->move_delay = player->move_delay_value;
13423 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13425 TestIfPlayerTouchesBadThing(jx, jy);
13426 TestIfPlayerTouchesCustomElement(jx, jy);
13429 if (!player->active)
13430 RemovePlayer(player);
13435 void ScrollPlayer(struct PlayerInfo *player, int mode)
13437 int jx = player->jx, jy = player->jy;
13438 int last_jx = player->last_jx, last_jy = player->last_jy;
13439 int move_stepsize = TILEX / player->move_delay_value;
13441 #if USE_NEW_PLAYER_SPEED
13442 if (!player->active)
13445 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13448 if (!player->active || player->MovPos == 0)
13452 if (mode == SCROLL_INIT)
13454 player->actual_frame_counter = FrameCounter;
13455 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13457 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13458 Feld[last_jx][last_jy] == EL_EMPTY)
13460 int last_field_block_delay = 0; /* start with no blocking at all */
13461 int block_delay_adjustment = player->block_delay_adjustment;
13463 /* if player blocks last field, add delay for exactly one move */
13464 if (player->block_last_field)
13466 last_field_block_delay += player->move_delay_value;
13468 /* when blocking enabled, prevent moving up despite gravity */
13469 #if USE_PLAYER_GRAVITY
13470 if (player->gravity && player->MovDir == MV_UP)
13471 block_delay_adjustment = -1;
13473 if (game.gravity && player->MovDir == MV_UP)
13474 block_delay_adjustment = -1;
13478 /* add block delay adjustment (also possible when not blocking) */
13479 last_field_block_delay += block_delay_adjustment;
13481 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13482 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13485 #if USE_NEW_PLAYER_SPEED
13486 if (player->MovPos != 0) /* player has not yet reached destination */
13492 else if (!FrameReached(&player->actual_frame_counter, 1))
13495 #if USE_NEW_PLAYER_SPEED
13496 if (player->MovPos != 0)
13498 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13499 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13501 /* before DrawPlayer() to draw correct player graphic for this case */
13502 if (player->MovPos == 0)
13503 CheckGravityMovement(player);
13506 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13507 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13509 /* before DrawPlayer() to draw correct player graphic for this case */
13510 if (player->MovPos == 0)
13511 CheckGravityMovement(player);
13514 if (player->MovPos == 0) /* player reached destination field */
13516 if (player->move_delay_reset_counter > 0)
13518 player->move_delay_reset_counter--;
13520 if (player->move_delay_reset_counter == 0)
13522 /* continue with normal speed after quickly moving through gate */
13523 HALVE_PLAYER_SPEED(player);
13525 /* be able to make the next move without delay */
13526 player->move_delay = 0;
13530 player->last_jx = jx;
13531 player->last_jy = jy;
13533 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13534 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13535 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13536 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13537 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13538 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13540 DrawPlayer(player); /* needed here only to cleanup last field */
13541 RemovePlayer(player);
13543 if (local_player->friends_still_needed == 0 ||
13544 IS_SP_ELEMENT(Feld[jx][jy]))
13545 PlayerWins(player);
13548 /* this breaks one level: "machine", level 000 */
13550 int move_direction = player->MovDir;
13551 int enter_side = MV_DIR_OPPOSITE(move_direction);
13552 int leave_side = move_direction;
13553 int old_jx = last_jx;
13554 int old_jy = last_jy;
13555 int old_element = Feld[old_jx][old_jy];
13556 int new_element = Feld[jx][jy];
13558 if (IS_CUSTOM_ELEMENT(old_element))
13559 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13561 player->index_bit, leave_side);
13563 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13564 CE_PLAYER_LEAVES_X,
13565 player->index_bit, leave_side);
13567 if (IS_CUSTOM_ELEMENT(new_element))
13568 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13569 player->index_bit, enter_side);
13571 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13572 CE_PLAYER_ENTERS_X,
13573 player->index_bit, enter_side);
13575 #if USE_FIX_CE_ACTION_WITH_PLAYER
13576 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13577 CE_MOVE_OF_X, move_direction);
13579 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13580 CE_MOVE_OF_X, move_direction);
13584 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13586 TestIfPlayerTouchesBadThing(jx, jy);
13587 TestIfPlayerTouchesCustomElement(jx, jy);
13589 /* needed because pushed element has not yet reached its destination,
13590 so it would trigger a change event at its previous field location */
13591 if (!player->is_pushing)
13592 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13594 if (!player->active)
13595 RemovePlayer(player);
13598 if (!local_player->LevelSolved && level.use_step_counter)
13608 if (TimeLeft <= 10 && setup.time_limit)
13609 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13612 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13614 DisplayGameControlValues();
13616 DrawGameValue_Time(TimeLeft);
13619 if (!TimeLeft && setup.time_limit)
13620 for (i = 0; i < MAX_PLAYERS; i++)
13621 KillPlayer(&stored_player[i]);
13624 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13626 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13628 DisplayGameControlValues();
13631 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13632 DrawGameValue_Time(TimePlayed);
13636 if (tape.single_step && tape.recording && !tape.pausing &&
13637 !player->programmed_action)
13638 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13642 void ScrollScreen(struct PlayerInfo *player, int mode)
13644 static unsigned long screen_frame_counter = 0;
13646 if (mode == SCROLL_INIT)
13648 /* set scrolling step size according to actual player's moving speed */
13649 ScrollStepSize = TILEX / player->move_delay_value;
13651 screen_frame_counter = FrameCounter;
13652 ScreenMovDir = player->MovDir;
13653 ScreenMovPos = player->MovPos;
13654 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13657 else if (!FrameReached(&screen_frame_counter, 1))
13662 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13663 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13664 redraw_mask |= REDRAW_FIELD;
13667 ScreenMovDir = MV_NONE;
13670 void TestIfPlayerTouchesCustomElement(int x, int y)
13672 static int xy[4][2] =
13679 static int trigger_sides[4][2] =
13681 /* center side border side */
13682 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13683 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13684 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13685 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13687 static int touch_dir[4] =
13689 MV_LEFT | MV_RIGHT,
13694 int center_element = Feld[x][y]; /* should always be non-moving! */
13697 for (i = 0; i < NUM_DIRECTIONS; i++)
13699 int xx = x + xy[i][0];
13700 int yy = y + xy[i][1];
13701 int center_side = trigger_sides[i][0];
13702 int border_side = trigger_sides[i][1];
13703 int border_element;
13705 if (!IN_LEV_FIELD(xx, yy))
13708 if (IS_PLAYER(x, y)) /* player found at center element */
13710 struct PlayerInfo *player = PLAYERINFO(x, y);
13712 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13713 border_element = Feld[xx][yy]; /* may be moving! */
13714 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13715 border_element = Feld[xx][yy];
13716 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13717 border_element = MovingOrBlocked2Element(xx, yy);
13719 continue; /* center and border element do not touch */
13721 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13722 player->index_bit, border_side);
13723 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13724 CE_PLAYER_TOUCHES_X,
13725 player->index_bit, border_side);
13727 #if USE_FIX_CE_ACTION_WITH_PLAYER
13729 /* use player element that is initially defined in the level playfield,
13730 not the player element that corresponds to the runtime player number
13731 (example: a level that contains EL_PLAYER_3 as the only player would
13732 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13733 int player_element = PLAYERINFO(x, y)->initial_element;
13735 CheckElementChangeBySide(xx, yy, border_element, player_element,
13736 CE_TOUCHING_X, border_side);
13740 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13742 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13744 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13746 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13747 continue; /* center and border element do not touch */
13750 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13751 player->index_bit, center_side);
13752 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13753 CE_PLAYER_TOUCHES_X,
13754 player->index_bit, center_side);
13756 #if USE_FIX_CE_ACTION_WITH_PLAYER
13758 /* use player element that is initially defined in the level playfield,
13759 not the player element that corresponds to the runtime player number
13760 (example: a level that contains EL_PLAYER_3 as the only player would
13761 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13762 int player_element = PLAYERINFO(xx, yy)->initial_element;
13764 CheckElementChangeBySide(x, y, center_element, player_element,
13765 CE_TOUCHING_X, center_side);
13774 #if USE_ELEMENT_TOUCHING_BUGFIX
13776 void TestIfElementTouchesCustomElement(int x, int y)
13778 static int xy[4][2] =
13785 static int trigger_sides[4][2] =
13787 /* center side border side */
13788 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13789 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13790 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13791 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13793 static int touch_dir[4] =
13795 MV_LEFT | MV_RIGHT,
13800 boolean change_center_element = FALSE;
13801 int center_element = Feld[x][y]; /* should always be non-moving! */
13802 int border_element_old[NUM_DIRECTIONS];
13805 for (i = 0; i < NUM_DIRECTIONS; i++)
13807 int xx = x + xy[i][0];
13808 int yy = y + xy[i][1];
13809 int border_element;
13811 border_element_old[i] = -1;
13813 if (!IN_LEV_FIELD(xx, yy))
13816 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13817 border_element = Feld[xx][yy]; /* may be moving! */
13818 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13819 border_element = Feld[xx][yy];
13820 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13821 border_element = MovingOrBlocked2Element(xx, yy);
13823 continue; /* center and border element do not touch */
13825 border_element_old[i] = border_element;
13828 for (i = 0; i < NUM_DIRECTIONS; i++)
13830 int xx = x + xy[i][0];
13831 int yy = y + xy[i][1];
13832 int center_side = trigger_sides[i][0];
13833 int border_element = border_element_old[i];
13835 if (border_element == -1)
13838 /* check for change of border element */
13839 CheckElementChangeBySide(xx, yy, border_element, center_element,
13840 CE_TOUCHING_X, center_side);
13842 /* (center element cannot be player, so we dont have to check this here) */
13845 for (i = 0; i < NUM_DIRECTIONS; i++)
13847 int xx = x + xy[i][0];
13848 int yy = y + xy[i][1];
13849 int border_side = trigger_sides[i][1];
13850 int border_element = border_element_old[i];
13852 if (border_element == -1)
13855 /* check for change of center element (but change it only once) */
13856 if (!change_center_element)
13857 change_center_element =
13858 CheckElementChangeBySide(x, y, center_element, border_element,
13859 CE_TOUCHING_X, border_side);
13861 #if USE_FIX_CE_ACTION_WITH_PLAYER
13862 if (IS_PLAYER(xx, yy))
13864 /* use player element that is initially defined in the level playfield,
13865 not the player element that corresponds to the runtime player number
13866 (example: a level that contains EL_PLAYER_3 as the only player would
13867 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13868 int player_element = PLAYERINFO(xx, yy)->initial_element;
13870 CheckElementChangeBySide(x, y, center_element, player_element,
13871 CE_TOUCHING_X, border_side);
13879 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13881 static int xy[4][2] =
13888 static int trigger_sides[4][2] =
13890 /* center side border side */
13891 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13892 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13893 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13894 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13896 static int touch_dir[4] =
13898 MV_LEFT | MV_RIGHT,
13903 boolean change_center_element = FALSE;
13904 int center_element = Feld[x][y]; /* should always be non-moving! */
13907 for (i = 0; i < NUM_DIRECTIONS; i++)
13909 int xx = x + xy[i][0];
13910 int yy = y + xy[i][1];
13911 int center_side = trigger_sides[i][0];
13912 int border_side = trigger_sides[i][1];
13913 int border_element;
13915 if (!IN_LEV_FIELD(xx, yy))
13918 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13919 border_element = Feld[xx][yy]; /* may be moving! */
13920 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13921 border_element = Feld[xx][yy];
13922 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13923 border_element = MovingOrBlocked2Element(xx, yy);
13925 continue; /* center and border element do not touch */
13927 /* check for change of center element (but change it only once) */
13928 if (!change_center_element)
13929 change_center_element =
13930 CheckElementChangeBySide(x, y, center_element, border_element,
13931 CE_TOUCHING_X, border_side);
13933 /* check for change of border element */
13934 CheckElementChangeBySide(xx, yy, border_element, center_element,
13935 CE_TOUCHING_X, center_side);
13941 void TestIfElementHitsCustomElement(int x, int y, int direction)
13943 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13944 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13945 int hitx = x + dx, hity = y + dy;
13946 int hitting_element = Feld[x][y];
13947 int touched_element;
13949 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13952 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13953 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13955 if (IN_LEV_FIELD(hitx, hity))
13957 int opposite_direction = MV_DIR_OPPOSITE(direction);
13958 int hitting_side = direction;
13959 int touched_side = opposite_direction;
13960 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13961 MovDir[hitx][hity] != direction ||
13962 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13968 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13969 CE_HITTING_X, touched_side);
13971 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13972 CE_HIT_BY_X, hitting_side);
13974 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13975 CE_HIT_BY_SOMETHING, opposite_direction);
13977 #if USE_FIX_CE_ACTION_WITH_PLAYER
13978 if (IS_PLAYER(hitx, hity))
13980 /* use player element that is initially defined in the level playfield,
13981 not the player element that corresponds to the runtime player number
13982 (example: a level that contains EL_PLAYER_3 as the only player would
13983 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13984 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13986 CheckElementChangeBySide(x, y, hitting_element, player_element,
13987 CE_HITTING_X, touched_side);
13993 /* "hitting something" is also true when hitting the playfield border */
13994 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13995 CE_HITTING_SOMETHING, direction);
13999 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14001 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14002 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14003 int hitx = x + dx, hity = y + dy;
14004 int hitting_element = Feld[x][y];
14005 int touched_element;
14007 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14008 !IS_FREE(hitx, hity) &&
14009 (!IS_MOVING(hitx, hity) ||
14010 MovDir[hitx][hity] != direction ||
14011 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14014 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14018 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14022 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14023 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14025 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14026 EP_CAN_SMASH_EVERYTHING, direction);
14028 if (IN_LEV_FIELD(hitx, hity))
14030 int opposite_direction = MV_DIR_OPPOSITE(direction);
14031 int hitting_side = direction;
14032 int touched_side = opposite_direction;
14034 int touched_element = MovingOrBlocked2Element(hitx, hity);
14037 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14038 MovDir[hitx][hity] != direction ||
14039 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14048 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14049 CE_SMASHED_BY_SOMETHING, opposite_direction);
14051 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14052 CE_OTHER_IS_SMASHING, touched_side);
14054 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14055 CE_OTHER_GETS_SMASHED, hitting_side);
14061 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14063 int i, kill_x = -1, kill_y = -1;
14065 int bad_element = -1;
14066 static int test_xy[4][2] =
14073 static int test_dir[4] =
14081 for (i = 0; i < NUM_DIRECTIONS; i++)
14083 int test_x, test_y, test_move_dir, test_element;
14085 test_x = good_x + test_xy[i][0];
14086 test_y = good_y + test_xy[i][1];
14088 if (!IN_LEV_FIELD(test_x, test_y))
14092 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14094 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14096 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14097 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14099 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14100 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14104 bad_element = test_element;
14110 if (kill_x != -1 || kill_y != -1)
14112 if (IS_PLAYER(good_x, good_y))
14114 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14116 if (player->shield_deadly_time_left > 0 &&
14117 !IS_INDESTRUCTIBLE(bad_element))
14118 Bang(kill_x, kill_y);
14119 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14120 KillPlayer(player);
14123 Bang(good_x, good_y);
14127 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14129 int i, kill_x = -1, kill_y = -1;
14130 int bad_element = Feld[bad_x][bad_y];
14131 static int test_xy[4][2] =
14138 static int touch_dir[4] =
14140 MV_LEFT | MV_RIGHT,
14145 static int test_dir[4] =
14153 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14156 for (i = 0; i < NUM_DIRECTIONS; i++)
14158 int test_x, test_y, test_move_dir, test_element;
14160 test_x = bad_x + test_xy[i][0];
14161 test_y = bad_y + test_xy[i][1];
14163 if (!IN_LEV_FIELD(test_x, test_y))
14167 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14169 test_element = Feld[test_x][test_y];
14171 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14172 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14174 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14175 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14177 /* good thing is player or penguin that does not move away */
14178 if (IS_PLAYER(test_x, test_y))
14180 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14182 if (bad_element == EL_ROBOT && player->is_moving)
14183 continue; /* robot does not kill player if he is moving */
14185 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14187 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14188 continue; /* center and border element do not touch */
14196 else if (test_element == EL_PENGUIN)
14206 if (kill_x != -1 || kill_y != -1)
14208 if (IS_PLAYER(kill_x, kill_y))
14210 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14212 if (player->shield_deadly_time_left > 0 &&
14213 !IS_INDESTRUCTIBLE(bad_element))
14214 Bang(bad_x, bad_y);
14215 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14216 KillPlayer(player);
14219 Bang(kill_x, kill_y);
14223 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14225 int bad_element = Feld[bad_x][bad_y];
14226 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14227 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14228 int test_x = bad_x + dx, test_y = bad_y + dy;
14229 int test_move_dir, test_element;
14230 int kill_x = -1, kill_y = -1;
14232 if (!IN_LEV_FIELD(test_x, test_y))
14236 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14238 test_element = Feld[test_x][test_y];
14240 if (test_move_dir != bad_move_dir)
14242 /* good thing can be player or penguin that does not move away */
14243 if (IS_PLAYER(test_x, test_y))
14245 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14247 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14248 player as being hit when he is moving towards the bad thing, because
14249 the "get hit by" condition would be lost after the player stops) */
14250 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14251 return; /* player moves away from bad thing */
14256 else if (test_element == EL_PENGUIN)
14263 if (kill_x != -1 || kill_y != -1)
14265 if (IS_PLAYER(kill_x, kill_y))
14267 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14269 if (player->shield_deadly_time_left > 0 &&
14270 !IS_INDESTRUCTIBLE(bad_element))
14271 Bang(bad_x, bad_y);
14272 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14273 KillPlayer(player);
14276 Bang(kill_x, kill_y);
14280 void TestIfPlayerTouchesBadThing(int x, int y)
14282 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14285 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14287 TestIfGoodThingHitsBadThing(x, y, move_dir);
14290 void TestIfBadThingTouchesPlayer(int x, int y)
14292 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14295 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14297 TestIfBadThingHitsGoodThing(x, y, move_dir);
14300 void TestIfFriendTouchesBadThing(int x, int y)
14302 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14305 void TestIfBadThingTouchesFriend(int x, int y)
14307 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14310 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14312 int i, kill_x = bad_x, kill_y = bad_y;
14313 static int xy[4][2] =
14321 for (i = 0; i < NUM_DIRECTIONS; i++)
14325 x = bad_x + xy[i][0];
14326 y = bad_y + xy[i][1];
14327 if (!IN_LEV_FIELD(x, y))
14330 element = Feld[x][y];
14331 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14332 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14340 if (kill_x != bad_x || kill_y != bad_y)
14341 Bang(bad_x, bad_y);
14344 void KillPlayer(struct PlayerInfo *player)
14346 int jx = player->jx, jy = player->jy;
14348 if (!player->active)
14352 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14353 player->killed, player->active, player->reanimated);
14356 /* the following code was introduced to prevent an infinite loop when calling
14358 -> CheckTriggeredElementChangeExt()
14359 -> ExecuteCustomElementAction()
14361 -> (infinitely repeating the above sequence of function calls)
14362 which occurs when killing the player while having a CE with the setting
14363 "kill player X when explosion of <player X>"; the solution using a new
14364 field "player->killed" was chosen for backwards compatibility, although
14365 clever use of the fields "player->active" etc. would probably also work */
14367 if (player->killed)
14371 player->killed = TRUE;
14373 /* remove accessible field at the player's position */
14374 Feld[jx][jy] = EL_EMPTY;
14376 /* deactivate shield (else Bang()/Explode() would not work right) */
14377 player->shield_normal_time_left = 0;
14378 player->shield_deadly_time_left = 0;
14381 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14382 player->killed, player->active, player->reanimated);
14388 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14389 player->killed, player->active, player->reanimated);
14392 #if USE_PLAYER_REANIMATION
14394 if (player->reanimated) /* killed player may have been reanimated */
14395 player->killed = player->reanimated = FALSE;
14397 BuryPlayer(player);
14399 if (player->killed) /* player may have been reanimated */
14400 BuryPlayer(player);
14403 BuryPlayer(player);
14407 static void KillPlayerUnlessEnemyProtected(int x, int y)
14409 if (!PLAYER_ENEMY_PROTECTED(x, y))
14410 KillPlayer(PLAYERINFO(x, y));
14413 static void KillPlayerUnlessExplosionProtected(int x, int y)
14415 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14416 KillPlayer(PLAYERINFO(x, y));
14419 void BuryPlayer(struct PlayerInfo *player)
14421 int jx = player->jx, jy = player->jy;
14423 if (!player->active)
14426 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14427 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14429 player->GameOver = TRUE;
14430 RemovePlayer(player);
14433 void RemovePlayer(struct PlayerInfo *player)
14435 int jx = player->jx, jy = player->jy;
14436 int i, found = FALSE;
14438 player->present = FALSE;
14439 player->active = FALSE;
14441 if (!ExplodeField[jx][jy])
14442 StorePlayer[jx][jy] = 0;
14444 if (player->is_moving)
14445 TEST_DrawLevelField(player->last_jx, player->last_jy);
14447 for (i = 0; i < MAX_PLAYERS; i++)
14448 if (stored_player[i].active)
14452 AllPlayersGone = TRUE;
14458 #if USE_NEW_SNAP_DELAY
14459 static void setFieldForSnapping(int x, int y, int element, int direction)
14461 struct ElementInfo *ei = &element_info[element];
14462 int direction_bit = MV_DIR_TO_BIT(direction);
14463 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14464 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14465 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14467 Feld[x][y] = EL_ELEMENT_SNAPPING;
14468 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14470 ResetGfxAnimation(x, y);
14472 GfxElement[x][y] = element;
14473 GfxAction[x][y] = action;
14474 GfxDir[x][y] = direction;
14475 GfxFrame[x][y] = -1;
14480 =============================================================================
14481 checkDiagonalPushing()
14482 -----------------------------------------------------------------------------
14483 check if diagonal input device direction results in pushing of object
14484 (by checking if the alternative direction is walkable, diggable, ...)
14485 =============================================================================
14488 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14489 int x, int y, int real_dx, int real_dy)
14491 int jx, jy, dx, dy, xx, yy;
14493 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14496 /* diagonal direction: check alternative direction */
14501 xx = jx + (dx == 0 ? real_dx : 0);
14502 yy = jy + (dy == 0 ? real_dy : 0);
14504 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14508 =============================================================================
14510 -----------------------------------------------------------------------------
14511 x, y: field next to player (non-diagonal) to try to dig to
14512 real_dx, real_dy: direction as read from input device (can be diagonal)
14513 =============================================================================
14516 static int DigField(struct PlayerInfo *player,
14517 int oldx, int oldy, int x, int y,
14518 int real_dx, int real_dy, int mode)
14520 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14521 boolean player_was_pushing = player->is_pushing;
14522 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14523 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14524 int jx = oldx, jy = oldy;
14525 int dx = x - jx, dy = y - jy;
14526 int nextx = x + dx, nexty = y + dy;
14527 int move_direction = (dx == -1 ? MV_LEFT :
14528 dx == +1 ? MV_RIGHT :
14530 dy == +1 ? MV_DOWN : MV_NONE);
14531 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14532 int dig_side = MV_DIR_OPPOSITE(move_direction);
14533 int old_element = Feld[jx][jy];
14534 #if USE_FIXED_DONT_RUN_INTO
14535 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14541 if (is_player) /* function can also be called by EL_PENGUIN */
14543 if (player->MovPos == 0)
14545 player->is_digging = FALSE;
14546 player->is_collecting = FALSE;
14549 if (player->MovPos == 0) /* last pushing move finished */
14550 player->is_pushing = FALSE;
14552 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14554 player->is_switching = FALSE;
14555 player->push_delay = -1;
14557 return MP_NO_ACTION;
14561 #if !USE_FIXED_DONT_RUN_INTO
14562 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14563 return MP_NO_ACTION;
14566 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14567 old_element = Back[jx][jy];
14569 /* in case of element dropped at player position, check background */
14570 else if (Back[jx][jy] != EL_EMPTY &&
14571 game.engine_version >= VERSION_IDENT(2,2,0,0))
14572 old_element = Back[jx][jy];
14574 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14575 return MP_NO_ACTION; /* field has no opening in this direction */
14577 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14578 return MP_NO_ACTION; /* field has no opening in this direction */
14580 #if USE_FIXED_DONT_RUN_INTO
14581 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14585 Feld[jx][jy] = player->artwork_element;
14586 InitMovingField(jx, jy, MV_DOWN);
14587 Store[jx][jy] = EL_ACID;
14588 ContinueMoving(jx, jy);
14589 BuryPlayer(player);
14591 return MP_DONT_RUN_INTO;
14595 #if USE_FIXED_DONT_RUN_INTO
14596 if (player_can_move && DONT_RUN_INTO(element))
14598 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14600 return MP_DONT_RUN_INTO;
14604 #if USE_FIXED_DONT_RUN_INTO
14605 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14606 return MP_NO_ACTION;
14609 #if !USE_FIXED_DONT_RUN_INTO
14610 element = Feld[x][y];
14613 collect_count = element_info[element].collect_count_initial;
14615 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14616 return MP_NO_ACTION;
14618 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14619 player_can_move = player_can_move_or_snap;
14621 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14622 game.engine_version >= VERSION_IDENT(2,2,0,0))
14624 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14625 player->index_bit, dig_side);
14626 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14627 player->index_bit, dig_side);
14629 if (element == EL_DC_LANDMINE)
14632 if (Feld[x][y] != element) /* field changed by snapping */
14635 return MP_NO_ACTION;
14638 #if USE_PLAYER_GRAVITY
14639 if (player->gravity && is_player && !player->is_auto_moving &&
14640 canFallDown(player) && move_direction != MV_DOWN &&
14641 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14642 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14644 if (game.gravity && is_player && !player->is_auto_moving &&
14645 canFallDown(player) && move_direction != MV_DOWN &&
14646 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14647 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14650 if (player_can_move &&
14651 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14653 int sound_element = SND_ELEMENT(element);
14654 int sound_action = ACTION_WALKING;
14656 if (IS_RND_GATE(element))
14658 if (!player->key[RND_GATE_NR(element)])
14659 return MP_NO_ACTION;
14661 else if (IS_RND_GATE_GRAY(element))
14663 if (!player->key[RND_GATE_GRAY_NR(element)])
14664 return MP_NO_ACTION;
14666 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14668 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14669 return MP_NO_ACTION;
14671 else if (element == EL_EXIT_OPEN ||
14672 element == EL_EM_EXIT_OPEN ||
14673 element == EL_STEEL_EXIT_OPEN ||
14674 element == EL_EM_STEEL_EXIT_OPEN ||
14675 element == EL_SP_EXIT_OPEN ||
14676 element == EL_SP_EXIT_OPENING)
14678 sound_action = ACTION_PASSING; /* player is passing exit */
14680 else if (element == EL_EMPTY)
14682 sound_action = ACTION_MOVING; /* nothing to walk on */
14685 /* play sound from background or player, whatever is available */
14686 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14687 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14689 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14691 else if (player_can_move &&
14692 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14694 if (!ACCESS_FROM(element, opposite_direction))
14695 return MP_NO_ACTION; /* field not accessible from this direction */
14697 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14698 return MP_NO_ACTION;
14700 if (IS_EM_GATE(element))
14702 if (!player->key[EM_GATE_NR(element)])
14703 return MP_NO_ACTION;
14705 else if (IS_EM_GATE_GRAY(element))
14707 if (!player->key[EM_GATE_GRAY_NR(element)])
14708 return MP_NO_ACTION;
14710 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14712 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14713 return MP_NO_ACTION;
14715 else if (IS_EMC_GATE(element))
14717 if (!player->key[EMC_GATE_NR(element)])
14718 return MP_NO_ACTION;
14720 else if (IS_EMC_GATE_GRAY(element))
14722 if (!player->key[EMC_GATE_GRAY_NR(element)])
14723 return MP_NO_ACTION;
14725 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14727 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14728 return MP_NO_ACTION;
14730 else if (element == EL_DC_GATE_WHITE ||
14731 element == EL_DC_GATE_WHITE_GRAY ||
14732 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14734 if (player->num_white_keys == 0)
14735 return MP_NO_ACTION;
14737 player->num_white_keys--;
14739 else if (IS_SP_PORT(element))
14741 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14742 element == EL_SP_GRAVITY_PORT_RIGHT ||
14743 element == EL_SP_GRAVITY_PORT_UP ||
14744 element == EL_SP_GRAVITY_PORT_DOWN)
14745 #if USE_PLAYER_GRAVITY
14746 player->gravity = !player->gravity;
14748 game.gravity = !game.gravity;
14750 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14751 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14752 element == EL_SP_GRAVITY_ON_PORT_UP ||
14753 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14754 #if USE_PLAYER_GRAVITY
14755 player->gravity = TRUE;
14757 game.gravity = TRUE;
14759 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14760 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14761 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14762 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14763 #if USE_PLAYER_GRAVITY
14764 player->gravity = FALSE;
14766 game.gravity = FALSE;
14770 /* automatically move to the next field with double speed */
14771 player->programmed_action = move_direction;
14773 if (player->move_delay_reset_counter == 0)
14775 player->move_delay_reset_counter = 2; /* two double speed steps */
14777 DOUBLE_PLAYER_SPEED(player);
14780 PlayLevelSoundAction(x, y, ACTION_PASSING);
14782 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14786 if (mode != DF_SNAP)
14788 GfxElement[x][y] = GFX_ELEMENT(element);
14789 player->is_digging = TRUE;
14792 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14794 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14795 player->index_bit, dig_side);
14797 if (mode == DF_SNAP)
14799 #if USE_NEW_SNAP_DELAY
14800 if (level.block_snap_field)
14801 setFieldForSnapping(x, y, element, move_direction);
14803 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14805 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14808 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14809 player->index_bit, dig_side);
14812 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14816 if (is_player && mode != DF_SNAP)
14818 GfxElement[x][y] = element;
14819 player->is_collecting = TRUE;
14822 if (element == EL_SPEED_PILL)
14824 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14826 else if (element == EL_EXTRA_TIME && level.time > 0)
14828 TimeLeft += level.extra_time;
14831 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14833 DisplayGameControlValues();
14835 DrawGameValue_Time(TimeLeft);
14838 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14840 player->shield_normal_time_left += level.shield_normal_time;
14841 if (element == EL_SHIELD_DEADLY)
14842 player->shield_deadly_time_left += level.shield_deadly_time;
14844 else if (element == EL_DYNAMITE ||
14845 element == EL_EM_DYNAMITE ||
14846 element == EL_SP_DISK_RED)
14848 if (player->inventory_size < MAX_INVENTORY_SIZE)
14849 player->inventory_element[player->inventory_size++] = element;
14851 DrawGameDoorValues();
14853 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14855 player->dynabomb_count++;
14856 player->dynabombs_left++;
14858 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14860 player->dynabomb_size++;
14862 else if (element == EL_DYNABOMB_INCREASE_POWER)
14864 player->dynabomb_xl = TRUE;
14866 else if (IS_KEY(element))
14868 player->key[KEY_NR(element)] = TRUE;
14870 DrawGameDoorValues();
14872 else if (element == EL_DC_KEY_WHITE)
14874 player->num_white_keys++;
14876 /* display white keys? */
14877 /* DrawGameDoorValues(); */
14879 else if (IS_ENVELOPE(element))
14881 player->show_envelope = element;
14883 else if (element == EL_EMC_LENSES)
14885 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14887 RedrawAllInvisibleElementsForLenses();
14889 else if (element == EL_EMC_MAGNIFIER)
14891 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14893 RedrawAllInvisibleElementsForMagnifier();
14895 else if (IS_DROPPABLE(element) ||
14896 IS_THROWABLE(element)) /* can be collected and dropped */
14900 if (collect_count == 0)
14901 player->inventory_infinite_element = element;
14903 for (i = 0; i < collect_count; i++)
14904 if (player->inventory_size < MAX_INVENTORY_SIZE)
14905 player->inventory_element[player->inventory_size++] = element;
14907 DrawGameDoorValues();
14909 else if (collect_count > 0)
14911 local_player->gems_still_needed -= collect_count;
14912 if (local_player->gems_still_needed < 0)
14913 local_player->gems_still_needed = 0;
14916 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14918 DisplayGameControlValues();
14920 DrawGameValue_Emeralds(local_player->gems_still_needed);
14924 RaiseScoreElement(element);
14925 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14928 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14929 player->index_bit, dig_side);
14931 if (mode == DF_SNAP)
14933 #if USE_NEW_SNAP_DELAY
14934 if (level.block_snap_field)
14935 setFieldForSnapping(x, y, element, move_direction);
14937 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14939 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14942 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14943 player->index_bit, dig_side);
14946 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14948 if (mode == DF_SNAP && element != EL_BD_ROCK)
14949 return MP_NO_ACTION;
14951 if (CAN_FALL(element) && dy)
14952 return MP_NO_ACTION;
14954 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14955 !(element == EL_SPRING && level.use_spring_bug))
14956 return MP_NO_ACTION;
14958 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14959 ((move_direction & MV_VERTICAL &&
14960 ((element_info[element].move_pattern & MV_LEFT &&
14961 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14962 (element_info[element].move_pattern & MV_RIGHT &&
14963 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14964 (move_direction & MV_HORIZONTAL &&
14965 ((element_info[element].move_pattern & MV_UP &&
14966 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14967 (element_info[element].move_pattern & MV_DOWN &&
14968 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14969 return MP_NO_ACTION;
14971 /* do not push elements already moving away faster than player */
14972 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14973 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14974 return MP_NO_ACTION;
14976 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14978 if (player->push_delay_value == -1 || !player_was_pushing)
14979 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14981 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14983 if (player->push_delay_value == -1)
14984 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14986 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14988 if (!player->is_pushing)
14989 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14992 player->is_pushing = TRUE;
14993 player->is_active = TRUE;
14995 if (!(IN_LEV_FIELD(nextx, nexty) &&
14996 (IS_FREE(nextx, nexty) ||
14997 (IS_SB_ELEMENT(element) &&
14998 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14999 (IS_CUSTOM_ELEMENT(element) &&
15000 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15001 return MP_NO_ACTION;
15003 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15004 return MP_NO_ACTION;
15006 if (player->push_delay == -1) /* new pushing; restart delay */
15007 player->push_delay = 0;
15009 if (player->push_delay < player->push_delay_value &&
15010 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15011 element != EL_SPRING && element != EL_BALLOON)
15013 /* make sure that there is no move delay before next try to push */
15014 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15015 player->move_delay = 0;
15017 return MP_NO_ACTION;
15020 if (IS_CUSTOM_ELEMENT(element) &&
15021 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15023 if (!DigFieldByCE(nextx, nexty, element))
15024 return MP_NO_ACTION;
15027 if (IS_SB_ELEMENT(element))
15029 if (element == EL_SOKOBAN_FIELD_FULL)
15031 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15032 local_player->sokobanfields_still_needed++;
15035 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15037 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15038 local_player->sokobanfields_still_needed--;
15041 Feld[x][y] = EL_SOKOBAN_OBJECT;
15043 if (Back[x][y] == Back[nextx][nexty])
15044 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15045 else if (Back[x][y] != 0)
15046 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15049 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15052 if (local_player->sokobanfields_still_needed == 0 &&
15053 game.emulation == EMU_SOKOBAN)
15055 PlayerWins(player);
15057 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15061 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15063 InitMovingField(x, y, move_direction);
15064 GfxAction[x][y] = ACTION_PUSHING;
15066 if (mode == DF_SNAP)
15067 ContinueMoving(x, y);
15069 MovPos[x][y] = (dx != 0 ? dx : dy);
15071 Pushed[x][y] = TRUE;
15072 Pushed[nextx][nexty] = TRUE;
15074 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15075 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15077 player->push_delay_value = -1; /* get new value later */
15079 /* check for element change _after_ element has been pushed */
15080 if (game.use_change_when_pushing_bug)
15082 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15083 player->index_bit, dig_side);
15084 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15085 player->index_bit, dig_side);
15088 else if (IS_SWITCHABLE(element))
15090 if (PLAYER_SWITCHING(player, x, y))
15092 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15093 player->index_bit, dig_side);
15098 player->is_switching = TRUE;
15099 player->switch_x = x;
15100 player->switch_y = y;
15102 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15104 if (element == EL_ROBOT_WHEEL)
15106 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15110 game.robot_wheel_active = TRUE;
15112 TEST_DrawLevelField(x, y);
15114 else if (element == EL_SP_TERMINAL)
15118 SCAN_PLAYFIELD(xx, yy)
15120 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15122 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15123 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15126 else if (IS_BELT_SWITCH(element))
15128 ToggleBeltSwitch(x, y);
15130 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15131 element == EL_SWITCHGATE_SWITCH_DOWN ||
15132 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15133 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15135 ToggleSwitchgateSwitch(x, y);
15137 else if (element == EL_LIGHT_SWITCH ||
15138 element == EL_LIGHT_SWITCH_ACTIVE)
15140 ToggleLightSwitch(x, y);
15142 else if (element == EL_TIMEGATE_SWITCH ||
15143 element == EL_DC_TIMEGATE_SWITCH)
15145 ActivateTimegateSwitch(x, y);
15147 else if (element == EL_BALLOON_SWITCH_LEFT ||
15148 element == EL_BALLOON_SWITCH_RIGHT ||
15149 element == EL_BALLOON_SWITCH_UP ||
15150 element == EL_BALLOON_SWITCH_DOWN ||
15151 element == EL_BALLOON_SWITCH_NONE ||
15152 element == EL_BALLOON_SWITCH_ANY)
15154 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15155 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15156 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15157 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15158 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15161 else if (element == EL_LAMP)
15163 Feld[x][y] = EL_LAMP_ACTIVE;
15164 local_player->lights_still_needed--;
15166 ResetGfxAnimation(x, y);
15167 TEST_DrawLevelField(x, y);
15169 else if (element == EL_TIME_ORB_FULL)
15171 Feld[x][y] = EL_TIME_ORB_EMPTY;
15173 if (level.time > 0 || level.use_time_orb_bug)
15175 TimeLeft += level.time_orb_time;
15178 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15180 DisplayGameControlValues();
15182 DrawGameValue_Time(TimeLeft);
15186 ResetGfxAnimation(x, y);
15187 TEST_DrawLevelField(x, y);
15189 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15190 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15194 game.ball_state = !game.ball_state;
15196 SCAN_PLAYFIELD(xx, yy)
15198 int e = Feld[xx][yy];
15200 if (game.ball_state)
15202 if (e == EL_EMC_MAGIC_BALL)
15203 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15204 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15205 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15209 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15210 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15211 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15212 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15217 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15218 player->index_bit, dig_side);
15220 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15221 player->index_bit, dig_side);
15223 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15224 player->index_bit, dig_side);
15230 if (!PLAYER_SWITCHING(player, x, y))
15232 player->is_switching = TRUE;
15233 player->switch_x = x;
15234 player->switch_y = y;
15236 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15237 player->index_bit, dig_side);
15238 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15239 player->index_bit, dig_side);
15241 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15242 player->index_bit, dig_side);
15243 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15244 player->index_bit, dig_side);
15247 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15248 player->index_bit, dig_side);
15249 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15250 player->index_bit, dig_side);
15252 return MP_NO_ACTION;
15255 player->push_delay = -1;
15257 if (is_player) /* function can also be called by EL_PENGUIN */
15259 if (Feld[x][y] != element) /* really digged/collected something */
15261 player->is_collecting = !player->is_digging;
15262 player->is_active = TRUE;
15269 static boolean DigFieldByCE(int x, int y, int digging_element)
15271 int element = Feld[x][y];
15273 if (!IS_FREE(x, y))
15275 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15276 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15279 /* no element can dig solid indestructible elements */
15280 if (IS_INDESTRUCTIBLE(element) &&
15281 !IS_DIGGABLE(element) &&
15282 !IS_COLLECTIBLE(element))
15285 if (AmoebaNr[x][y] &&
15286 (element == EL_AMOEBA_FULL ||
15287 element == EL_BD_AMOEBA ||
15288 element == EL_AMOEBA_GROWING))
15290 AmoebaCnt[AmoebaNr[x][y]]--;
15291 AmoebaCnt2[AmoebaNr[x][y]]--;
15294 if (IS_MOVING(x, y))
15295 RemoveMovingField(x, y);
15299 TEST_DrawLevelField(x, y);
15302 /* if digged element was about to explode, prevent the explosion */
15303 ExplodeField[x][y] = EX_TYPE_NONE;
15305 PlayLevelSoundAction(x, y, action);
15308 Store[x][y] = EL_EMPTY;
15311 /* this makes it possible to leave the removed element again */
15312 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15313 Store[x][y] = element;
15315 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15317 int move_leave_element = element_info[digging_element].move_leave_element;
15319 /* this makes it possible to leave the removed element again */
15320 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15321 element : move_leave_element);
15328 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15330 int jx = player->jx, jy = player->jy;
15331 int x = jx + dx, y = jy + dy;
15332 int snap_direction = (dx == -1 ? MV_LEFT :
15333 dx == +1 ? MV_RIGHT :
15335 dy == +1 ? MV_DOWN : MV_NONE);
15336 boolean can_continue_snapping = (level.continuous_snapping &&
15337 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15339 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15342 if (!player->active || !IN_LEV_FIELD(x, y))
15350 if (player->MovPos == 0)
15351 player->is_pushing = FALSE;
15353 player->is_snapping = FALSE;
15355 if (player->MovPos == 0)
15357 player->is_moving = FALSE;
15358 player->is_digging = FALSE;
15359 player->is_collecting = FALSE;
15365 #if USE_NEW_CONTINUOUS_SNAPPING
15366 /* prevent snapping with already pressed snap key when not allowed */
15367 if (player->is_snapping && !can_continue_snapping)
15370 if (player->is_snapping)
15374 player->MovDir = snap_direction;
15376 if (player->MovPos == 0)
15378 player->is_moving = FALSE;
15379 player->is_digging = FALSE;
15380 player->is_collecting = FALSE;
15383 player->is_dropping = FALSE;
15384 player->is_dropping_pressed = FALSE;
15385 player->drop_pressed_delay = 0;
15387 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15390 player->is_snapping = TRUE;
15391 player->is_active = TRUE;
15393 if (player->MovPos == 0)
15395 player->is_moving = FALSE;
15396 player->is_digging = FALSE;
15397 player->is_collecting = FALSE;
15400 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15401 TEST_DrawLevelField(player->last_jx, player->last_jy);
15403 TEST_DrawLevelField(x, y);
15408 static boolean DropElement(struct PlayerInfo *player)
15410 int old_element, new_element;
15411 int dropx = player->jx, dropy = player->jy;
15412 int drop_direction = player->MovDir;
15413 int drop_side = drop_direction;
15415 int drop_element = get_next_dropped_element(player);
15417 int drop_element = (player->inventory_size > 0 ?
15418 player->inventory_element[player->inventory_size - 1] :
15419 player->inventory_infinite_element != EL_UNDEFINED ?
15420 player->inventory_infinite_element :
15421 player->dynabombs_left > 0 ?
15422 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15426 player->is_dropping_pressed = TRUE;
15428 /* do not drop an element on top of another element; when holding drop key
15429 pressed without moving, dropped element must move away before the next
15430 element can be dropped (this is especially important if the next element
15431 is dynamite, which can be placed on background for historical reasons) */
15432 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15435 if (IS_THROWABLE(drop_element))
15437 dropx += GET_DX_FROM_DIR(drop_direction);
15438 dropy += GET_DY_FROM_DIR(drop_direction);
15440 if (!IN_LEV_FIELD(dropx, dropy))
15444 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15445 new_element = drop_element; /* default: no change when dropping */
15447 /* check if player is active, not moving and ready to drop */
15448 if (!player->active || player->MovPos || player->drop_delay > 0)
15451 /* check if player has anything that can be dropped */
15452 if (new_element == EL_UNDEFINED)
15455 /* check if drop key was pressed long enough for EM style dynamite */
15456 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15459 /* check if anything can be dropped at the current position */
15460 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15463 /* collected custom elements can only be dropped on empty fields */
15464 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15467 if (old_element != EL_EMPTY)
15468 Back[dropx][dropy] = old_element; /* store old element on this field */
15470 ResetGfxAnimation(dropx, dropy);
15471 ResetRandomAnimationValue(dropx, dropy);
15473 if (player->inventory_size > 0 ||
15474 player->inventory_infinite_element != EL_UNDEFINED)
15476 if (player->inventory_size > 0)
15478 player->inventory_size--;
15480 DrawGameDoorValues();
15482 if (new_element == EL_DYNAMITE)
15483 new_element = EL_DYNAMITE_ACTIVE;
15484 else if (new_element == EL_EM_DYNAMITE)
15485 new_element = EL_EM_DYNAMITE_ACTIVE;
15486 else if (new_element == EL_SP_DISK_RED)
15487 new_element = EL_SP_DISK_RED_ACTIVE;
15490 Feld[dropx][dropy] = new_element;
15492 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15493 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15494 el2img(Feld[dropx][dropy]), 0);
15496 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15498 /* needed if previous element just changed to "empty" in the last frame */
15499 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15501 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15502 player->index_bit, drop_side);
15503 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15505 player->index_bit, drop_side);
15507 TestIfElementTouchesCustomElement(dropx, dropy);
15509 else /* player is dropping a dyna bomb */
15511 player->dynabombs_left--;
15513 Feld[dropx][dropy] = new_element;
15515 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15516 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15517 el2img(Feld[dropx][dropy]), 0);
15519 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15522 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15523 InitField_WithBug1(dropx, dropy, FALSE);
15525 new_element = Feld[dropx][dropy]; /* element might have changed */
15527 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15528 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15530 int move_direction, nextx, nexty;
15532 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15533 MovDir[dropx][dropy] = drop_direction;
15535 move_direction = MovDir[dropx][dropy];
15536 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15537 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15539 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15541 #if USE_FIX_IMPACT_COLLISION
15542 /* do not cause impact style collision by dropping elements that can fall */
15543 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15545 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15549 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15550 player->is_dropping = TRUE;
15552 player->drop_pressed_delay = 0;
15553 player->is_dropping_pressed = FALSE;
15555 player->drop_x = dropx;
15556 player->drop_y = dropy;
15561 /* ------------------------------------------------------------------------- */
15562 /* game sound playing functions */
15563 /* ------------------------------------------------------------------------- */
15565 static int *loop_sound_frame = NULL;
15566 static int *loop_sound_volume = NULL;
15568 void InitPlayLevelSound()
15570 int num_sounds = getSoundListSize();
15572 checked_free(loop_sound_frame);
15573 checked_free(loop_sound_volume);
15575 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15576 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15579 static void PlayLevelSound(int x, int y, int nr)
15581 int sx = SCREENX(x), sy = SCREENY(y);
15582 int volume, stereo_position;
15583 int max_distance = 8;
15584 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15586 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15587 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15590 if (!IN_LEV_FIELD(x, y) ||
15591 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15592 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15595 volume = SOUND_MAX_VOLUME;
15597 if (!IN_SCR_FIELD(sx, sy))
15599 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15600 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15602 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15605 stereo_position = (SOUND_MAX_LEFT +
15606 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15607 (SCR_FIELDX + 2 * max_distance));
15609 if (IS_LOOP_SOUND(nr))
15611 /* This assures that quieter loop sounds do not overwrite louder ones,
15612 while restarting sound volume comparison with each new game frame. */
15614 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15617 loop_sound_volume[nr] = volume;
15618 loop_sound_frame[nr] = FrameCounter;
15621 PlaySoundExt(nr, volume, stereo_position, type);
15624 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15626 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15627 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15628 y < LEVELY(BY1) ? LEVELY(BY1) :
15629 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15633 static void PlayLevelSoundAction(int x, int y, int action)
15635 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15638 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15640 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15642 if (sound_effect != SND_UNDEFINED)
15643 PlayLevelSound(x, y, sound_effect);
15646 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15649 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15651 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15652 PlayLevelSound(x, y, sound_effect);
15655 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15657 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15659 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15660 PlayLevelSound(x, y, sound_effect);
15663 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15665 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15667 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15668 StopSound(sound_effect);
15671 static void PlayLevelMusic()
15673 if (levelset.music[level_nr] != MUS_UNDEFINED)
15674 PlayMusic(levelset.music[level_nr]); /* from config file */
15676 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15679 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15681 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15682 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15683 int x = xx - 1 - offset;
15684 int y = yy - 1 - offset;
15689 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15693 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15697 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15701 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15705 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15709 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15713 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15716 case SAMPLE_android_clone:
15717 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15720 case SAMPLE_android_move:
15721 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15724 case SAMPLE_spring:
15725 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15729 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15733 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15736 case SAMPLE_eater_eat:
15737 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15741 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15744 case SAMPLE_collect:
15745 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15748 case SAMPLE_diamond:
15749 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15752 case SAMPLE_squash:
15753 /* !!! CHECK THIS !!! */
15755 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15757 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15761 case SAMPLE_wonderfall:
15762 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15766 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15770 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15774 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15778 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15782 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15786 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15789 case SAMPLE_wonder:
15790 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15794 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15797 case SAMPLE_exit_open:
15798 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15801 case SAMPLE_exit_leave:
15802 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15805 case SAMPLE_dynamite:
15806 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15810 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15814 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15818 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15822 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15826 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15830 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15834 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15840 void ChangeTime(int value)
15842 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15846 /* EMC game engine uses value from time counter of RND game engine */
15847 level.native_em_level->lev->time = *time;
15849 DrawGameValue_Time(*time);
15852 void RaiseScore(int value)
15854 /* EMC game engine and RND game engine have separate score counters */
15855 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15856 &level.native_em_level->lev->score : &local_player->score);
15860 DrawGameValue_Score(*score);
15864 void RaiseScore(int value)
15866 local_player->score += value;
15869 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15871 DisplayGameControlValues();
15873 DrawGameValue_Score(local_player->score);
15877 void RaiseScoreElement(int element)
15882 case EL_BD_DIAMOND:
15883 case EL_EMERALD_YELLOW:
15884 case EL_EMERALD_RED:
15885 case EL_EMERALD_PURPLE:
15886 case EL_SP_INFOTRON:
15887 RaiseScore(level.score[SC_EMERALD]);
15890 RaiseScore(level.score[SC_DIAMOND]);
15893 RaiseScore(level.score[SC_CRYSTAL]);
15896 RaiseScore(level.score[SC_PEARL]);
15899 case EL_BD_BUTTERFLY:
15900 case EL_SP_ELECTRON:
15901 RaiseScore(level.score[SC_BUG]);
15904 case EL_BD_FIREFLY:
15905 case EL_SP_SNIKSNAK:
15906 RaiseScore(level.score[SC_SPACESHIP]);
15909 case EL_DARK_YAMYAM:
15910 RaiseScore(level.score[SC_YAMYAM]);
15913 RaiseScore(level.score[SC_ROBOT]);
15916 RaiseScore(level.score[SC_PACMAN]);
15919 RaiseScore(level.score[SC_NUT]);
15922 case EL_EM_DYNAMITE:
15923 case EL_SP_DISK_RED:
15924 case EL_DYNABOMB_INCREASE_NUMBER:
15925 case EL_DYNABOMB_INCREASE_SIZE:
15926 case EL_DYNABOMB_INCREASE_POWER:
15927 RaiseScore(level.score[SC_DYNAMITE]);
15929 case EL_SHIELD_NORMAL:
15930 case EL_SHIELD_DEADLY:
15931 RaiseScore(level.score[SC_SHIELD]);
15933 case EL_EXTRA_TIME:
15934 RaiseScore(level.extra_time_score);
15948 case EL_DC_KEY_WHITE:
15949 RaiseScore(level.score[SC_KEY]);
15952 RaiseScore(element_info[element].collect_score);
15957 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15959 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15961 #if defined(NETWORK_AVALIABLE)
15962 if (options.network)
15963 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15972 FadeSkipNextFadeIn();
15974 fading = fading_none;
15978 OpenDoor(DOOR_CLOSE_1);
15981 game_status = GAME_MODE_MAIN;
15984 DrawAndFadeInMainMenu(REDRAW_FIELD);
15992 FadeOut(REDRAW_FIELD);
15995 game_status = GAME_MODE_MAIN;
15997 DrawAndFadeInMainMenu(REDRAW_FIELD);
16001 else /* continue playing the game */
16003 if (tape.playing && tape.deactivate_display)
16004 TapeDeactivateDisplayOff(TRUE);
16006 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16008 if (tape.playing && tape.deactivate_display)
16009 TapeDeactivateDisplayOn();
16013 void RequestQuitGame(boolean ask_if_really_quit)
16015 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16016 boolean skip_request = AllPlayersGone || quick_quit;
16018 RequestQuitGameExt(skip_request, quick_quit,
16019 "Do you really want to quit the game ?");
16023 /* ------------------------------------------------------------------------- */
16024 /* random generator functions */
16025 /* ------------------------------------------------------------------------- */
16027 unsigned int InitEngineRandom_RND(long seed)
16029 game.num_random_calls = 0;
16032 unsigned int rnd_seed = InitEngineRandom(seed);
16034 printf("::: START RND: %d\n", rnd_seed);
16039 return InitEngineRandom(seed);
16045 unsigned int RND(int max)
16049 game.num_random_calls++;
16051 return GetEngineRandom(max);
16058 /* ------------------------------------------------------------------------- */
16059 /* game engine snapshot handling functions */
16060 /* ------------------------------------------------------------------------- */
16062 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16064 struct EngineSnapshotInfo
16066 /* runtime values for custom element collect score */
16067 int collect_score[NUM_CUSTOM_ELEMENTS];
16069 /* runtime values for group element choice position */
16070 int choice_pos[NUM_GROUP_ELEMENTS];
16072 /* runtime values for belt position animations */
16073 int belt_graphic[4 * NUM_BELT_PARTS];
16074 int belt_anim_mode[4 * NUM_BELT_PARTS];
16077 struct EngineSnapshotNodeInfo
16084 static struct EngineSnapshotInfo engine_snapshot_rnd;
16085 static ListNode *engine_snapshot_list = NULL;
16086 static char *snapshot_level_identifier = NULL;
16087 static int snapshot_level_nr = -1;
16089 void FreeEngineSnapshot()
16091 while (engine_snapshot_list != NULL)
16092 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16095 setString(&snapshot_level_identifier, NULL);
16096 snapshot_level_nr = -1;
16099 static void SaveEngineSnapshotValues_RND()
16101 static int belt_base_active_element[4] =
16103 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16104 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16105 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16106 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16110 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16112 int element = EL_CUSTOM_START + i;
16114 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16117 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16119 int element = EL_GROUP_START + i;
16121 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16124 for (i = 0; i < 4; i++)
16126 for (j = 0; j < NUM_BELT_PARTS; j++)
16128 int element = belt_base_active_element[i] + j;
16129 int graphic = el2img(element);
16130 int anim_mode = graphic_info[graphic].anim_mode;
16132 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16133 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16138 static void LoadEngineSnapshotValues_RND()
16140 unsigned long num_random_calls = game.num_random_calls;
16143 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16145 int element = EL_CUSTOM_START + i;
16147 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16150 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16152 int element = EL_GROUP_START + i;
16154 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16157 for (i = 0; i < 4; i++)
16159 for (j = 0; j < NUM_BELT_PARTS; j++)
16161 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16162 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16164 graphic_info[graphic].anim_mode = anim_mode;
16168 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16170 InitRND(tape.random_seed);
16171 for (i = 0; i < num_random_calls; i++)
16175 if (game.num_random_calls != num_random_calls)
16177 Error(ERR_INFO, "number of random calls out of sync");
16178 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16179 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16180 Error(ERR_EXIT, "this should not happen -- please debug");
16184 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16186 struct EngineSnapshotNodeInfo *bi =
16187 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16189 bi->buffer_orig = buffer;
16190 bi->buffer_copy = checked_malloc(size);
16193 memcpy(bi->buffer_copy, buffer, size);
16195 addNodeToList(&engine_snapshot_list, NULL, bi);
16198 void SaveEngineSnapshot()
16200 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16202 if (level_editor_test_game) /* do not save snapshots from editor */
16205 /* copy some special values to a structure better suited for the snapshot */
16207 SaveEngineSnapshotValues_RND();
16208 SaveEngineSnapshotValues_EM();
16210 /* save values stored in special snapshot structure */
16212 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16213 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16215 /* save further RND engine values */
16217 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16218 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16219 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16221 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16222 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16223 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16224 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16226 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16227 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16228 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16229 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16230 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16232 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16233 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16234 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16236 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16238 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16240 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16241 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16243 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16244 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16245 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16246 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16247 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16248 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16249 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16250 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16251 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16252 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16253 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16254 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16255 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16256 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16257 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16258 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16259 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16260 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16262 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16263 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16265 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16266 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16267 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16269 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16270 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16272 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16273 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16274 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16275 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16276 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16278 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16279 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16281 /* save level identification information */
16283 setString(&snapshot_level_identifier, leveldir_current->identifier);
16284 snapshot_level_nr = level_nr;
16287 ListNode *node = engine_snapshot_list;
16290 while (node != NULL)
16292 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16297 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16301 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16303 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16306 void LoadEngineSnapshot()
16308 ListNode *node = engine_snapshot_list;
16310 if (engine_snapshot_list == NULL)
16313 while (node != NULL)
16315 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16320 /* restore special values from snapshot structure */
16322 LoadEngineSnapshotValues_RND();
16323 LoadEngineSnapshotValues_EM();
16326 boolean CheckEngineSnapshot()
16328 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16329 snapshot_level_nr == level_nr);
16333 /* ---------- new game button stuff ---------------------------------------- */
16335 /* graphic position values for game buttons */
16336 #define GAME_BUTTON_XSIZE 30
16337 #define GAME_BUTTON_YSIZE 30
16338 #define GAME_BUTTON_XPOS 5
16339 #define GAME_BUTTON_YPOS 215
16340 #define SOUND_BUTTON_XPOS 5
16341 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16343 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16344 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16345 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16346 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16347 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16348 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16356 } gamebutton_info[NUM_GAME_BUTTONS] =
16360 &game.button.stop.x, &game.button.stop.y,
16361 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16366 &game.button.pause.x, &game.button.pause.y,
16367 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16368 GAME_CTRL_ID_PAUSE,
16372 &game.button.play.x, &game.button.play.y,
16373 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16378 &game.button.sound_music.x, &game.button.sound_music.y,
16379 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16380 SOUND_CTRL_ID_MUSIC,
16381 "background music on/off"
16384 &game.button.sound_loops.x, &game.button.sound_loops.y,
16385 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16386 SOUND_CTRL_ID_LOOPS,
16387 "sound loops on/off"
16390 &game.button.sound_simple.x,&game.button.sound_simple.y,
16391 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16392 SOUND_CTRL_ID_SIMPLE,
16393 "normal sounds on/off"
16397 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16402 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16403 GAME_CTRL_ID_PAUSE,
16407 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16412 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16413 SOUND_CTRL_ID_MUSIC,
16414 "background music on/off"
16417 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16418 SOUND_CTRL_ID_LOOPS,
16419 "sound loops on/off"
16422 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16423 SOUND_CTRL_ID_SIMPLE,
16424 "normal sounds on/off"
16429 void CreateGameButtons()
16433 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16435 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16436 struct GadgetInfo *gi;
16439 unsigned long event_mask;
16441 int gd_xoffset, gd_yoffset;
16442 int gd_x1, gd_x2, gd_y1, gd_y2;
16445 x = DX + *gamebutton_info[i].x;
16446 y = DY + *gamebutton_info[i].y;
16447 gd_xoffset = gamebutton_info[i].gd_x;
16448 gd_yoffset = gamebutton_info[i].gd_y;
16449 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16450 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16452 if (id == GAME_CTRL_ID_STOP ||
16453 id == GAME_CTRL_ID_PAUSE ||
16454 id == GAME_CTRL_ID_PLAY)
16456 button_type = GD_TYPE_NORMAL_BUTTON;
16458 event_mask = GD_EVENT_RELEASED;
16459 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16460 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16464 button_type = GD_TYPE_CHECK_BUTTON;
16466 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16467 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16468 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16469 event_mask = GD_EVENT_PRESSED;
16470 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16471 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16474 gi = CreateGadget(GDI_CUSTOM_ID, id,
16475 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16480 GDI_X, DX + gd_xoffset,
16481 GDI_Y, DY + gd_yoffset,
16483 GDI_WIDTH, GAME_BUTTON_XSIZE,
16484 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16485 GDI_TYPE, button_type,
16486 GDI_STATE, GD_BUTTON_UNPRESSED,
16487 GDI_CHECKED, checked,
16488 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16489 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16490 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16491 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16492 GDI_DIRECT_DRAW, FALSE,
16493 GDI_EVENT_MASK, event_mask,
16494 GDI_CALLBACK_ACTION, HandleGameButtons,
16498 Error(ERR_EXIT, "cannot create gadget");
16500 game_gadget[id] = gi;
16504 void FreeGameButtons()
16508 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16509 FreeGadget(game_gadget[i]);
16512 static void MapGameButtons()
16516 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16517 MapGadget(game_gadget[i]);
16520 void UnmapGameButtons()
16524 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16525 UnmapGadget(game_gadget[i]);
16528 void RedrawGameButtons()
16532 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16533 RedrawGadget(game_gadget[i]);
16536 static void HandleGameButtons(struct GadgetInfo *gi)
16538 int id = gi->custom_id;
16540 if (game_status != GAME_MODE_PLAYING)
16545 case GAME_CTRL_ID_STOP:
16549 RequestQuitGame(TRUE);
16552 case GAME_CTRL_ID_PAUSE:
16553 if (options.network)
16555 #if defined(NETWORK_AVALIABLE)
16557 SendToServer_ContinuePlaying();
16559 SendToServer_PausePlaying();
16563 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16566 case GAME_CTRL_ID_PLAY:
16569 #if defined(NETWORK_AVALIABLE)
16570 if (options.network)
16571 SendToServer_ContinuePlaying();
16575 tape.pausing = FALSE;
16576 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16581 case SOUND_CTRL_ID_MUSIC:
16582 if (setup.sound_music)
16584 setup.sound_music = FALSE;
16587 else if (audio.music_available)
16589 setup.sound = setup.sound_music = TRUE;
16591 SetAudioMode(setup.sound);
16597 case SOUND_CTRL_ID_LOOPS:
16598 if (setup.sound_loops)
16599 setup.sound_loops = FALSE;
16600 else if (audio.loops_available)
16602 setup.sound = setup.sound_loops = TRUE;
16603 SetAudioMode(setup.sound);
16607 case SOUND_CTRL_ID_SIMPLE:
16608 if (setup.sound_simple)
16609 setup.sound_simple = FALSE;
16610 else if (audio.sound_available)
16612 setup.sound = setup.sound_simple = TRUE;
16613 SetAudioMode(setup.sound);