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)
63 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
65 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
67 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
69 #if USE_DELAYED_GFX_REDRAW
70 #define TEST_DrawLevelField(x, y) \
71 GfxRedraw[x][y] |= GFX_REDRAW_TILE
72 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
73 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
74 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
75 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
76 #define TEST_DrawTwinkleOnField(x, y) \
77 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
79 #define TEST_DrawLevelField(x, y) \
81 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
82 DrawLevelFieldCrumbledSand(x, y)
83 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
84 DrawLevelFieldCrumbledSandNeighbours(x, y)
85 #define TEST_DrawTwinkleOnField(x, y) \
86 DrawTwinkleOnField(x, y)
95 /* for MovePlayer() */
96 #define MP_NO_ACTION 0
99 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
101 /* for ScrollPlayer() */
102 #define SCROLL_INIT 0
103 #define SCROLL_GO_ON 1
105 /* for Bang()/Explode() */
106 #define EX_PHASE_START 0
107 #define EX_TYPE_NONE 0
108 #define EX_TYPE_NORMAL (1 << 0)
109 #define EX_TYPE_CENTER (1 << 1)
110 #define EX_TYPE_BORDER (1 << 2)
111 #define EX_TYPE_CROSS (1 << 3)
112 #define EX_TYPE_DYNA (1 << 4)
113 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
115 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
116 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
117 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
118 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
120 /* special positions in the game control window (relative to control window) */
121 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
122 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
123 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
124 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
125 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
126 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
127 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
128 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
129 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
130 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
131 #define XX_SCORE (PANEL_XPOS(game.panel.score))
132 #define YY_SCORE (PANEL_YPOS(game.panel.score))
133 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
134 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
135 #define XX_TIME (PANEL_XPOS(game.panel.time))
136 #define YY_TIME (PANEL_YPOS(game.panel.time))
138 /* special positions in the game control window (relative to main window) */
139 #define DX_LEVEL1 (DX + XX_LEVEL1)
140 #define DX_LEVEL2 (DX + XX_LEVEL2)
141 #define DX_LEVEL (DX + XX_LEVEL)
142 #define DY_LEVEL (DY + YY_LEVEL)
143 #define DX_EMERALDS (DX + XX_EMERALDS)
144 #define DY_EMERALDS (DY + YY_EMERALDS)
145 #define DX_DYNAMITE (DX + XX_DYNAMITE)
146 #define DY_DYNAMITE (DY + YY_DYNAMITE)
147 #define DX_KEYS (DX + XX_KEYS)
148 #define DY_KEYS (DY + YY_KEYS)
149 #define DX_SCORE (DX + XX_SCORE)
150 #define DY_SCORE (DY + YY_SCORE)
151 #define DX_TIME1 (DX + XX_TIME1)
152 #define DX_TIME2 (DX + XX_TIME2)
153 #define DX_TIME (DX + XX_TIME)
154 #define DY_TIME (DY + YY_TIME)
157 /* game panel display and control definitions */
159 #define GAME_PANEL_LEVEL_NUMBER 0
160 #define GAME_PANEL_GEMS 1
161 #define GAME_PANEL_INVENTORY_COUNT 2
162 #define GAME_PANEL_INVENTORY_FIRST_1 3
163 #define GAME_PANEL_INVENTORY_FIRST_2 4
164 #define GAME_PANEL_INVENTORY_FIRST_3 5
165 #define GAME_PANEL_INVENTORY_FIRST_4 6
166 #define GAME_PANEL_INVENTORY_FIRST_5 7
167 #define GAME_PANEL_INVENTORY_FIRST_6 8
168 #define GAME_PANEL_INVENTORY_FIRST_7 9
169 #define GAME_PANEL_INVENTORY_FIRST_8 10
170 #define GAME_PANEL_INVENTORY_LAST_1 11
171 #define GAME_PANEL_INVENTORY_LAST_2 12
172 #define GAME_PANEL_INVENTORY_LAST_3 13
173 #define GAME_PANEL_INVENTORY_LAST_4 14
174 #define GAME_PANEL_INVENTORY_LAST_5 15
175 #define GAME_PANEL_INVENTORY_LAST_6 16
176 #define GAME_PANEL_INVENTORY_LAST_7 17
177 #define GAME_PANEL_INVENTORY_LAST_8 18
178 #define GAME_PANEL_KEY_1 19
179 #define GAME_PANEL_KEY_2 20
180 #define GAME_PANEL_KEY_3 21
181 #define GAME_PANEL_KEY_4 22
182 #define GAME_PANEL_KEY_5 23
183 #define GAME_PANEL_KEY_6 24
184 #define GAME_PANEL_KEY_7 25
185 #define GAME_PANEL_KEY_8 26
186 #define GAME_PANEL_KEY_WHITE 27
187 #define GAME_PANEL_KEY_WHITE_COUNT 28
188 #define GAME_PANEL_SCORE 29
189 #define GAME_PANEL_HIGHSCORE 30
190 #define GAME_PANEL_TIME 31
191 #define GAME_PANEL_TIME_HH 32
192 #define GAME_PANEL_TIME_MM 33
193 #define GAME_PANEL_TIME_SS 34
194 #define GAME_PANEL_SHIELD_NORMAL 35
195 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
196 #define GAME_PANEL_SHIELD_DEADLY 37
197 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
198 #define GAME_PANEL_EXIT 39
199 #define GAME_PANEL_EMC_MAGIC_BALL 40
200 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
201 #define GAME_PANEL_LIGHT_SWITCH 42
202 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
203 #define GAME_PANEL_TIMEGATE_SWITCH 44
204 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
205 #define GAME_PANEL_SWITCHGATE_SWITCH 46
206 #define GAME_PANEL_EMC_LENSES 47
207 #define GAME_PANEL_EMC_LENSES_TIME 48
208 #define GAME_PANEL_EMC_MAGNIFIER 49
209 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
210 #define GAME_PANEL_BALLOON_SWITCH 51
211 #define GAME_PANEL_DYNABOMB_NUMBER 52
212 #define GAME_PANEL_DYNABOMB_SIZE 53
213 #define GAME_PANEL_DYNABOMB_POWER 54
214 #define GAME_PANEL_PENGUINS 55
215 #define GAME_PANEL_SOKOBAN_OBJECTS 56
216 #define GAME_PANEL_SOKOBAN_FIELDS 57
217 #define GAME_PANEL_ROBOT_WHEEL 58
218 #define GAME_PANEL_CONVEYOR_BELT_1 59
219 #define GAME_PANEL_CONVEYOR_BELT_2 60
220 #define GAME_PANEL_CONVEYOR_BELT_3 61
221 #define GAME_PANEL_CONVEYOR_BELT_4 62
222 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
223 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
224 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
225 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
226 #define GAME_PANEL_MAGIC_WALL 67
227 #define GAME_PANEL_MAGIC_WALL_TIME 68
228 #define GAME_PANEL_GRAVITY_STATE 69
229 #define GAME_PANEL_GRAPHIC_1 70
230 #define GAME_PANEL_GRAPHIC_2 71
231 #define GAME_PANEL_GRAPHIC_3 72
232 #define GAME_PANEL_GRAPHIC_4 73
233 #define GAME_PANEL_GRAPHIC_5 74
234 #define GAME_PANEL_GRAPHIC_6 75
235 #define GAME_PANEL_GRAPHIC_7 76
236 #define GAME_PANEL_GRAPHIC_8 77
237 #define GAME_PANEL_ELEMENT_1 78
238 #define GAME_PANEL_ELEMENT_2 79
239 #define GAME_PANEL_ELEMENT_3 80
240 #define GAME_PANEL_ELEMENT_4 81
241 #define GAME_PANEL_ELEMENT_5 82
242 #define GAME_PANEL_ELEMENT_6 83
243 #define GAME_PANEL_ELEMENT_7 84
244 #define GAME_PANEL_ELEMENT_8 85
245 #define GAME_PANEL_ELEMENT_COUNT_1 86
246 #define GAME_PANEL_ELEMENT_COUNT_2 87
247 #define GAME_PANEL_ELEMENT_COUNT_3 88
248 #define GAME_PANEL_ELEMENT_COUNT_4 89
249 #define GAME_PANEL_ELEMENT_COUNT_5 90
250 #define GAME_PANEL_ELEMENT_COUNT_6 91
251 #define GAME_PANEL_ELEMENT_COUNT_7 92
252 #define GAME_PANEL_ELEMENT_COUNT_8 93
253 #define GAME_PANEL_CE_SCORE_1 94
254 #define GAME_PANEL_CE_SCORE_2 95
255 #define GAME_PANEL_CE_SCORE_3 96
256 #define GAME_PANEL_CE_SCORE_4 97
257 #define GAME_PANEL_CE_SCORE_5 98
258 #define GAME_PANEL_CE_SCORE_6 99
259 #define GAME_PANEL_CE_SCORE_7 100
260 #define GAME_PANEL_CE_SCORE_8 101
261 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
262 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
263 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
264 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
265 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
266 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
267 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
268 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
269 #define GAME_PANEL_PLAYER_NAME 110
270 #define GAME_PANEL_LEVEL_NAME 111
271 #define GAME_PANEL_LEVEL_AUTHOR 112
273 #define NUM_GAME_PANEL_CONTROLS 113
275 struct GamePanelOrderInfo
281 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
283 struct GamePanelControlInfo
287 struct TextPosInfo *pos;
290 int value, last_value;
291 int frame, last_frame;
296 static struct GamePanelControlInfo game_panel_controls[] =
299 GAME_PANEL_LEVEL_NUMBER,
300 &game.panel.level_number,
309 GAME_PANEL_INVENTORY_COUNT,
310 &game.panel.inventory_count,
314 GAME_PANEL_INVENTORY_FIRST_1,
315 &game.panel.inventory_first[0],
319 GAME_PANEL_INVENTORY_FIRST_2,
320 &game.panel.inventory_first[1],
324 GAME_PANEL_INVENTORY_FIRST_3,
325 &game.panel.inventory_first[2],
329 GAME_PANEL_INVENTORY_FIRST_4,
330 &game.panel.inventory_first[3],
334 GAME_PANEL_INVENTORY_FIRST_5,
335 &game.panel.inventory_first[4],
339 GAME_PANEL_INVENTORY_FIRST_6,
340 &game.panel.inventory_first[5],
344 GAME_PANEL_INVENTORY_FIRST_7,
345 &game.panel.inventory_first[6],
349 GAME_PANEL_INVENTORY_FIRST_8,
350 &game.panel.inventory_first[7],
354 GAME_PANEL_INVENTORY_LAST_1,
355 &game.panel.inventory_last[0],
359 GAME_PANEL_INVENTORY_LAST_2,
360 &game.panel.inventory_last[1],
364 GAME_PANEL_INVENTORY_LAST_3,
365 &game.panel.inventory_last[2],
369 GAME_PANEL_INVENTORY_LAST_4,
370 &game.panel.inventory_last[3],
374 GAME_PANEL_INVENTORY_LAST_5,
375 &game.panel.inventory_last[4],
379 GAME_PANEL_INVENTORY_LAST_6,
380 &game.panel.inventory_last[5],
384 GAME_PANEL_INVENTORY_LAST_7,
385 &game.panel.inventory_last[6],
389 GAME_PANEL_INVENTORY_LAST_8,
390 &game.panel.inventory_last[7],
434 GAME_PANEL_KEY_WHITE,
435 &game.panel.key_white,
439 GAME_PANEL_KEY_WHITE_COUNT,
440 &game.panel.key_white_count,
449 GAME_PANEL_HIGHSCORE,
450 &game.panel.highscore,
474 GAME_PANEL_SHIELD_NORMAL,
475 &game.panel.shield_normal,
479 GAME_PANEL_SHIELD_NORMAL_TIME,
480 &game.panel.shield_normal_time,
484 GAME_PANEL_SHIELD_DEADLY,
485 &game.panel.shield_deadly,
489 GAME_PANEL_SHIELD_DEADLY_TIME,
490 &game.panel.shield_deadly_time,
499 GAME_PANEL_EMC_MAGIC_BALL,
500 &game.panel.emc_magic_ball,
504 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
505 &game.panel.emc_magic_ball_switch,
509 GAME_PANEL_LIGHT_SWITCH,
510 &game.panel.light_switch,
514 GAME_PANEL_LIGHT_SWITCH_TIME,
515 &game.panel.light_switch_time,
519 GAME_PANEL_TIMEGATE_SWITCH,
520 &game.panel.timegate_switch,
524 GAME_PANEL_TIMEGATE_SWITCH_TIME,
525 &game.panel.timegate_switch_time,
529 GAME_PANEL_SWITCHGATE_SWITCH,
530 &game.panel.switchgate_switch,
534 GAME_PANEL_EMC_LENSES,
535 &game.panel.emc_lenses,
539 GAME_PANEL_EMC_LENSES_TIME,
540 &game.panel.emc_lenses_time,
544 GAME_PANEL_EMC_MAGNIFIER,
545 &game.panel.emc_magnifier,
549 GAME_PANEL_EMC_MAGNIFIER_TIME,
550 &game.panel.emc_magnifier_time,
554 GAME_PANEL_BALLOON_SWITCH,
555 &game.panel.balloon_switch,
559 GAME_PANEL_DYNABOMB_NUMBER,
560 &game.panel.dynabomb_number,
564 GAME_PANEL_DYNABOMB_SIZE,
565 &game.panel.dynabomb_size,
569 GAME_PANEL_DYNABOMB_POWER,
570 &game.panel.dynabomb_power,
575 &game.panel.penguins,
579 GAME_PANEL_SOKOBAN_OBJECTS,
580 &game.panel.sokoban_objects,
584 GAME_PANEL_SOKOBAN_FIELDS,
585 &game.panel.sokoban_fields,
589 GAME_PANEL_ROBOT_WHEEL,
590 &game.panel.robot_wheel,
594 GAME_PANEL_CONVEYOR_BELT_1,
595 &game.panel.conveyor_belt[0],
599 GAME_PANEL_CONVEYOR_BELT_2,
600 &game.panel.conveyor_belt[1],
604 GAME_PANEL_CONVEYOR_BELT_3,
605 &game.panel.conveyor_belt[2],
609 GAME_PANEL_CONVEYOR_BELT_4,
610 &game.panel.conveyor_belt[3],
614 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
615 &game.panel.conveyor_belt_switch[0],
619 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
620 &game.panel.conveyor_belt_switch[1],
624 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
625 &game.panel.conveyor_belt_switch[2],
629 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
630 &game.panel.conveyor_belt_switch[3],
634 GAME_PANEL_MAGIC_WALL,
635 &game.panel.magic_wall,
639 GAME_PANEL_MAGIC_WALL_TIME,
640 &game.panel.magic_wall_time,
644 GAME_PANEL_GRAVITY_STATE,
645 &game.panel.gravity_state,
649 GAME_PANEL_GRAPHIC_1,
650 &game.panel.graphic[0],
654 GAME_PANEL_GRAPHIC_2,
655 &game.panel.graphic[1],
659 GAME_PANEL_GRAPHIC_3,
660 &game.panel.graphic[2],
664 GAME_PANEL_GRAPHIC_4,
665 &game.panel.graphic[3],
669 GAME_PANEL_GRAPHIC_5,
670 &game.panel.graphic[4],
674 GAME_PANEL_GRAPHIC_6,
675 &game.panel.graphic[5],
679 GAME_PANEL_GRAPHIC_7,
680 &game.panel.graphic[6],
684 GAME_PANEL_GRAPHIC_8,
685 &game.panel.graphic[7],
689 GAME_PANEL_ELEMENT_1,
690 &game.panel.element[0],
694 GAME_PANEL_ELEMENT_2,
695 &game.panel.element[1],
699 GAME_PANEL_ELEMENT_3,
700 &game.panel.element[2],
704 GAME_PANEL_ELEMENT_4,
705 &game.panel.element[3],
709 GAME_PANEL_ELEMENT_5,
710 &game.panel.element[4],
714 GAME_PANEL_ELEMENT_6,
715 &game.panel.element[5],
719 GAME_PANEL_ELEMENT_7,
720 &game.panel.element[6],
724 GAME_PANEL_ELEMENT_8,
725 &game.panel.element[7],
729 GAME_PANEL_ELEMENT_COUNT_1,
730 &game.panel.element_count[0],
734 GAME_PANEL_ELEMENT_COUNT_2,
735 &game.panel.element_count[1],
739 GAME_PANEL_ELEMENT_COUNT_3,
740 &game.panel.element_count[2],
744 GAME_PANEL_ELEMENT_COUNT_4,
745 &game.panel.element_count[3],
749 GAME_PANEL_ELEMENT_COUNT_5,
750 &game.panel.element_count[4],
754 GAME_PANEL_ELEMENT_COUNT_6,
755 &game.panel.element_count[5],
759 GAME_PANEL_ELEMENT_COUNT_7,
760 &game.panel.element_count[6],
764 GAME_PANEL_ELEMENT_COUNT_8,
765 &game.panel.element_count[7],
769 GAME_PANEL_CE_SCORE_1,
770 &game.panel.ce_score[0],
774 GAME_PANEL_CE_SCORE_2,
775 &game.panel.ce_score[1],
779 GAME_PANEL_CE_SCORE_3,
780 &game.panel.ce_score[2],
784 GAME_PANEL_CE_SCORE_4,
785 &game.panel.ce_score[3],
789 GAME_PANEL_CE_SCORE_5,
790 &game.panel.ce_score[4],
794 GAME_PANEL_CE_SCORE_6,
795 &game.panel.ce_score[5],
799 GAME_PANEL_CE_SCORE_7,
800 &game.panel.ce_score[6],
804 GAME_PANEL_CE_SCORE_8,
805 &game.panel.ce_score[7],
809 GAME_PANEL_CE_SCORE_1_ELEMENT,
810 &game.panel.ce_score_element[0],
814 GAME_PANEL_CE_SCORE_2_ELEMENT,
815 &game.panel.ce_score_element[1],
819 GAME_PANEL_CE_SCORE_3_ELEMENT,
820 &game.panel.ce_score_element[2],
824 GAME_PANEL_CE_SCORE_4_ELEMENT,
825 &game.panel.ce_score_element[3],
829 GAME_PANEL_CE_SCORE_5_ELEMENT,
830 &game.panel.ce_score_element[4],
834 GAME_PANEL_CE_SCORE_6_ELEMENT,
835 &game.panel.ce_score_element[5],
839 GAME_PANEL_CE_SCORE_7_ELEMENT,
840 &game.panel.ce_score_element[6],
844 GAME_PANEL_CE_SCORE_8_ELEMENT,
845 &game.panel.ce_score_element[7],
849 GAME_PANEL_PLAYER_NAME,
850 &game.panel.player_name,
854 GAME_PANEL_LEVEL_NAME,
855 &game.panel.level_name,
859 GAME_PANEL_LEVEL_AUTHOR,
860 &game.panel.level_author,
873 /* values for delayed check of falling and moving elements and for collision */
874 #define CHECK_DELAY_MOVING 3
875 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
876 #define CHECK_DELAY_COLLISION 2
877 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
879 /* values for initial player move delay (initial delay counter value) */
880 #define INITIAL_MOVE_DELAY_OFF -1
881 #define INITIAL_MOVE_DELAY_ON 0
883 /* values for player movement speed (which is in fact a delay value) */
884 #define MOVE_DELAY_MIN_SPEED 32
885 #define MOVE_DELAY_NORMAL_SPEED 8
886 #define MOVE_DELAY_HIGH_SPEED 4
887 #define MOVE_DELAY_MAX_SPEED 1
889 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
890 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
892 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
893 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
895 /* values for other actions */
896 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
897 #define MOVE_STEPSIZE_MIN (1)
898 #define MOVE_STEPSIZE_MAX (TILEX)
900 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
901 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
903 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
905 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
906 RND(element_info[e].push_delay_random))
907 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
908 RND(element_info[e].drop_delay_random))
909 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
910 RND(element_info[e].move_delay_random))
911 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
912 (element_info[e].move_delay_random))
913 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
914 RND(element_info[e].ce_value_random_initial))
915 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
916 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
917 RND((c)->delay_random * (c)->delay_frames))
918 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
919 RND((c)->delay_random))
922 #define GET_VALID_RUNTIME_ELEMENT(e) \
923 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
925 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
926 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
927 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
928 (be) + (e) - EL_SELF)
930 #define GET_PLAYER_FROM_BITS(p) \
931 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
933 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
934 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
935 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
936 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
937 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
938 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
939 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
940 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
941 RESOLVED_REFERENCE_ELEMENT(be, e) : \
944 #define CAN_GROW_INTO(e) \
945 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
947 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
948 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
951 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
952 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
953 (CAN_MOVE_INTO_ACID(e) && \
954 Feld[x][y] == EL_ACID) || \
957 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
958 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
959 (CAN_MOVE_INTO_ACID(e) && \
960 Feld[x][y] == EL_ACID) || \
963 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
964 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
966 (CAN_MOVE_INTO_ACID(e) && \
967 Feld[x][y] == EL_ACID) || \
968 (DONT_COLLIDE_WITH(e) && \
970 !PLAYER_ENEMY_PROTECTED(x, y))))
972 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
973 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
975 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
978 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
981 #define ANDROID_CAN_CLONE_FIELD(x, y) \
982 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
983 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
985 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
986 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
988 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
989 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
991 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
994 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
997 #define PIG_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1000 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1002 Feld[x][y] == EL_EM_EXIT_OPEN || \
1003 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1004 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1005 IS_FOOD_PENGUIN(Feld[x][y])))
1006 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1007 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1009 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1012 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1013 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1015 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1016 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1017 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1019 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1021 #define CE_ENTER_FIELD_COND(e, x, y) \
1022 (!IS_PLAYER(x, y) && \
1023 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1025 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1026 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1028 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1029 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1031 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1032 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1033 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1034 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1036 /* game button identifiers */
1037 #define GAME_CTRL_ID_STOP 0
1038 #define GAME_CTRL_ID_PAUSE 1
1039 #define GAME_CTRL_ID_PLAY 2
1040 #define SOUND_CTRL_ID_MUSIC 3
1041 #define SOUND_CTRL_ID_LOOPS 4
1042 #define SOUND_CTRL_ID_SIMPLE 5
1044 #define NUM_GAME_BUTTONS 6
1047 /* forward declaration for internal use */
1049 static void CreateField(int, int, int);
1051 static void ResetGfxAnimation(int, int);
1053 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1054 static void AdvanceFrameAndPlayerCounters(int);
1056 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1057 static boolean MovePlayer(struct PlayerInfo *, int, int);
1058 static void ScrollPlayer(struct PlayerInfo *, int);
1059 static void ScrollScreen(struct PlayerInfo *, int);
1061 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1062 static boolean DigFieldByCE(int, int, int);
1063 static boolean SnapField(struct PlayerInfo *, int, int);
1064 static boolean DropElement(struct PlayerInfo *);
1066 static void InitBeltMovement(void);
1067 static void CloseAllOpenTimegates(void);
1068 static void CheckGravityMovement(struct PlayerInfo *);
1069 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1070 static void KillPlayerUnlessEnemyProtected(int, int);
1071 static void KillPlayerUnlessExplosionProtected(int, int);
1073 static void TestIfPlayerTouchesCustomElement(int, int);
1074 static void TestIfElementTouchesCustomElement(int, int);
1075 static void TestIfElementHitsCustomElement(int, int, int);
1077 static void TestIfElementSmashesCustomElement(int, int, int);
1080 static void HandleElementChange(int, int, int);
1081 static void ExecuteCustomElementAction(int, int, int, int);
1082 static boolean ChangeElement(int, int, int, int);
1084 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1085 #define CheckTriggeredElementChange(x, y, e, ev) \
1086 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1087 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1088 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1089 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1090 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1091 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1092 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1094 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1095 #define CheckElementChange(x, y, e, te, ev) \
1096 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1097 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1098 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1099 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1100 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1102 static void PlayLevelSound(int, int, int);
1103 static void PlayLevelSoundNearest(int, int, int);
1104 static void PlayLevelSoundAction(int, int, int);
1105 static void PlayLevelSoundElementAction(int, int, int, int);
1106 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1107 static void PlayLevelSoundActionIfLoop(int, int, int);
1108 static void StopLevelSoundActionIfLoop(int, int, int);
1109 static void PlayLevelMusic();
1111 static void MapGameButtons();
1112 static void HandleGameButtons(struct GadgetInfo *);
1114 int AmoebeNachbarNr(int, int);
1115 void AmoebeUmwandeln(int, int);
1116 void ContinueMoving(int, int);
1117 void Bang(int, int);
1118 void InitMovDir(int, int);
1119 void InitAmoebaNr(int, int);
1120 int NewHiScore(void);
1122 void TestIfGoodThingHitsBadThing(int, int, int);
1123 void TestIfBadThingHitsGoodThing(int, int, int);
1124 void TestIfPlayerTouchesBadThing(int, int);
1125 void TestIfPlayerRunsIntoBadThing(int, int, int);
1126 void TestIfBadThingTouchesPlayer(int, int);
1127 void TestIfBadThingRunsIntoPlayer(int, int, int);
1128 void TestIfFriendTouchesBadThing(int, int);
1129 void TestIfBadThingTouchesFriend(int, int);
1130 void TestIfBadThingTouchesOtherBadThing(int, int);
1131 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1133 void KillPlayer(struct PlayerInfo *);
1134 void BuryPlayer(struct PlayerInfo *);
1135 void RemovePlayer(struct PlayerInfo *);
1137 static int getInvisibleActiveFromInvisibleElement(int);
1138 static int getInvisibleFromInvisibleActiveElement(int);
1140 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1142 /* for detection of endless loops, caused by custom element programming */
1143 /* (using maximal playfield width x 10 is just a rough approximation) */
1144 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1146 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1148 if (recursion_loop_detected) \
1151 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1153 recursion_loop_detected = TRUE; \
1154 recursion_loop_element = (e); \
1157 recursion_loop_depth++; \
1160 #define RECURSION_LOOP_DETECTION_END() \
1162 recursion_loop_depth--; \
1165 static int recursion_loop_depth;
1166 static boolean recursion_loop_detected;
1167 static boolean recursion_loop_element;
1170 /* ------------------------------------------------------------------------- */
1171 /* definition of elements that automatically change to other elements after */
1172 /* a specified time, eventually calling a function when changing */
1173 /* ------------------------------------------------------------------------- */
1175 /* forward declaration for changer functions */
1176 static void InitBuggyBase(int, int);
1177 static void WarnBuggyBase(int, int);
1179 static void InitTrap(int, int);
1180 static void ActivateTrap(int, int);
1181 static void ChangeActiveTrap(int, int);
1183 static void InitRobotWheel(int, int);
1184 static void RunRobotWheel(int, int);
1185 static void StopRobotWheel(int, int);
1187 static void InitTimegateWheel(int, int);
1188 static void RunTimegateWheel(int, int);
1190 static void InitMagicBallDelay(int, int);
1191 static void ActivateMagicBall(int, int);
1193 struct ChangingElementInfo
1198 void (*pre_change_function)(int x, int y);
1199 void (*change_function)(int x, int y);
1200 void (*post_change_function)(int x, int y);
1203 static struct ChangingElementInfo change_delay_list[] =
1238 EL_STEEL_EXIT_OPENING,
1246 EL_STEEL_EXIT_CLOSING,
1247 EL_STEEL_EXIT_CLOSED,
1274 EL_EM_STEEL_EXIT_OPENING,
1275 EL_EM_STEEL_EXIT_OPEN,
1282 EL_EM_STEEL_EXIT_CLOSING,
1286 EL_EM_STEEL_EXIT_CLOSED,
1310 EL_SWITCHGATE_OPENING,
1318 EL_SWITCHGATE_CLOSING,
1319 EL_SWITCHGATE_CLOSED,
1326 EL_TIMEGATE_OPENING,
1334 EL_TIMEGATE_CLOSING,
1343 EL_ACID_SPLASH_LEFT,
1351 EL_ACID_SPLASH_RIGHT,
1360 EL_SP_BUGGY_BASE_ACTIVATING,
1367 EL_SP_BUGGY_BASE_ACTIVATING,
1368 EL_SP_BUGGY_BASE_ACTIVE,
1375 EL_SP_BUGGY_BASE_ACTIVE,
1399 EL_ROBOT_WHEEL_ACTIVE,
1407 EL_TIMEGATE_SWITCH_ACTIVE,
1415 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1416 EL_DC_TIMEGATE_SWITCH,
1423 EL_EMC_MAGIC_BALL_ACTIVE,
1424 EL_EMC_MAGIC_BALL_ACTIVE,
1431 EL_EMC_SPRING_BUMPER_ACTIVE,
1432 EL_EMC_SPRING_BUMPER,
1439 EL_DIAGONAL_SHRINKING,
1447 EL_DIAGONAL_GROWING,
1468 int push_delay_fixed, push_delay_random;
1472 { EL_SPRING, 0, 0 },
1473 { EL_BALLOON, 0, 0 },
1475 { EL_SOKOBAN_OBJECT, 2, 0 },
1476 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1477 { EL_SATELLITE, 2, 0 },
1478 { EL_SP_DISK_YELLOW, 2, 0 },
1480 { EL_UNDEFINED, 0, 0 },
1488 move_stepsize_list[] =
1490 { EL_AMOEBA_DROP, 2 },
1491 { EL_AMOEBA_DROPPING, 2 },
1492 { EL_QUICKSAND_FILLING, 1 },
1493 { EL_QUICKSAND_EMPTYING, 1 },
1494 { EL_QUICKSAND_FAST_FILLING, 2 },
1495 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1496 { EL_MAGIC_WALL_FILLING, 2 },
1497 { EL_MAGIC_WALL_EMPTYING, 2 },
1498 { EL_BD_MAGIC_WALL_FILLING, 2 },
1499 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1500 { EL_DC_MAGIC_WALL_FILLING, 2 },
1501 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_UNDEFINED, 0 },
1511 collect_count_list[] =
1514 { EL_BD_DIAMOND, 1 },
1515 { EL_EMERALD_YELLOW, 1 },
1516 { EL_EMERALD_RED, 1 },
1517 { EL_EMERALD_PURPLE, 1 },
1519 { EL_SP_INFOTRON, 1 },
1523 { EL_UNDEFINED, 0 },
1531 access_direction_list[] =
1533 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1534 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1535 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1536 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1537 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1538 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1539 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1540 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1541 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1542 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1543 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1545 { EL_SP_PORT_LEFT, MV_RIGHT },
1546 { EL_SP_PORT_RIGHT, MV_LEFT },
1547 { EL_SP_PORT_UP, MV_DOWN },
1548 { EL_SP_PORT_DOWN, MV_UP },
1549 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1550 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1551 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1552 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1553 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1554 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1555 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1556 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1557 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1558 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1559 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1560 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1561 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1562 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1563 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1565 { EL_UNDEFINED, MV_NONE }
1568 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1570 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1571 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1572 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1573 IS_JUST_CHANGING(x, y))
1575 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1577 /* static variables for playfield scan mode (scanning forward or backward) */
1578 static int playfield_scan_start_x = 0;
1579 static int playfield_scan_start_y = 0;
1580 static int playfield_scan_delta_x = 1;
1581 static int playfield_scan_delta_y = 1;
1583 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1584 (y) >= 0 && (y) <= lev_fieldy - 1; \
1585 (y) += playfield_scan_delta_y) \
1586 for ((x) = playfield_scan_start_x; \
1587 (x) >= 0 && (x) <= lev_fieldx - 1; \
1588 (x) += playfield_scan_delta_x)
1591 void DEBUG_SetMaximumDynamite()
1595 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1596 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1597 local_player->inventory_element[local_player->inventory_size++] =
1602 static void InitPlayfieldScanModeVars()
1604 if (game.use_reverse_scan_direction)
1606 playfield_scan_start_x = lev_fieldx - 1;
1607 playfield_scan_start_y = lev_fieldy - 1;
1609 playfield_scan_delta_x = -1;
1610 playfield_scan_delta_y = -1;
1614 playfield_scan_start_x = 0;
1615 playfield_scan_start_y = 0;
1617 playfield_scan_delta_x = 1;
1618 playfield_scan_delta_y = 1;
1622 static void InitPlayfieldScanMode(int mode)
1624 game.use_reverse_scan_direction =
1625 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1627 InitPlayfieldScanModeVars();
1630 static int get_move_delay_from_stepsize(int move_stepsize)
1633 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1635 /* make sure that stepsize value is always a power of 2 */
1636 move_stepsize = (1 << log_2(move_stepsize));
1638 return TILEX / move_stepsize;
1641 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1644 int player_nr = player->index_nr;
1645 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1646 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1648 /* do no immediately change move delay -- the player might just be moving */
1649 player->move_delay_value_next = move_delay;
1651 /* information if player can move must be set separately */
1652 player->cannot_move = cannot_move;
1656 player->move_delay = game.initial_move_delay[player_nr];
1657 player->move_delay_value = game.initial_move_delay_value[player_nr];
1659 player->move_delay_value_next = -1;
1661 player->move_delay_reset_counter = 0;
1665 void GetPlayerConfig()
1667 GameFrameDelay = setup.game_frame_delay;
1669 if (!audio.sound_available)
1670 setup.sound_simple = FALSE;
1672 if (!audio.loops_available)
1673 setup.sound_loops = FALSE;
1675 if (!audio.music_available)
1676 setup.sound_music = FALSE;
1678 if (!video.fullscreen_available)
1679 setup.fullscreen = FALSE;
1681 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1683 SetAudioMode(setup.sound);
1687 int GetElementFromGroupElement(int element)
1689 if (IS_GROUP_ELEMENT(element))
1691 struct ElementGroupInfo *group = element_info[element].group;
1692 int last_anim_random_frame = gfx.anim_random_frame;
1695 if (group->choice_mode == ANIM_RANDOM)
1696 gfx.anim_random_frame = RND(group->num_elements_resolved);
1698 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1699 group->choice_mode, 0,
1702 if (group->choice_mode == ANIM_RANDOM)
1703 gfx.anim_random_frame = last_anim_random_frame;
1705 group->choice_pos++;
1707 element = group->element_resolved[element_pos];
1713 static void InitPlayerField(int x, int y, int element, boolean init_game)
1715 if (element == EL_SP_MURPHY)
1719 if (stored_player[0].present)
1721 Feld[x][y] = EL_SP_MURPHY_CLONE;
1727 stored_player[0].initial_element = element;
1728 stored_player[0].use_murphy = TRUE;
1730 if (!level.use_artwork_element[0])
1731 stored_player[0].artwork_element = EL_SP_MURPHY;
1734 Feld[x][y] = EL_PLAYER_1;
1740 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1741 int jx = player->jx, jy = player->jy;
1743 player->present = TRUE;
1745 player->block_last_field = (element == EL_SP_MURPHY ?
1746 level.sp_block_last_field :
1747 level.block_last_field);
1749 /* ---------- initialize player's last field block delay --------------- */
1751 /* always start with reliable default value (no adjustment needed) */
1752 player->block_delay_adjustment = 0;
1754 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1755 if (player->block_last_field && element == EL_SP_MURPHY)
1756 player->block_delay_adjustment = 1;
1758 /* special case 2: in game engines before 3.1.1, blocking was different */
1759 if (game.use_block_last_field_bug)
1760 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1762 if (!options.network || player->connected)
1764 player->active = TRUE;
1766 /* remove potentially duplicate players */
1767 if (StorePlayer[jx][jy] == Feld[x][y])
1768 StorePlayer[jx][jy] = 0;
1770 StorePlayer[x][y] = Feld[x][y];
1774 printf("Player %d activated.\n", player->element_nr);
1775 printf("[Local player is %d and currently %s.]\n",
1776 local_player->element_nr,
1777 local_player->active ? "active" : "not active");
1781 Feld[x][y] = EL_EMPTY;
1783 player->jx = player->last_jx = x;
1784 player->jy = player->last_jy = y;
1787 #if USE_PLAYER_REANIMATION
1790 int player_nr = GET_PLAYER_NR(element);
1791 struct PlayerInfo *player = &stored_player[player_nr];
1794 player->killed = FALSE; /* if player was just killed, reanimate him */
1799 static void InitField(int x, int y, boolean init_game)
1801 int element = Feld[x][y];
1810 InitPlayerField(x, y, element, init_game);
1813 case EL_SOKOBAN_FIELD_PLAYER:
1814 element = Feld[x][y] = EL_PLAYER_1;
1815 InitField(x, y, init_game);
1817 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1818 InitField(x, y, init_game);
1821 case EL_SOKOBAN_FIELD_EMPTY:
1822 local_player->sokobanfields_still_needed++;
1826 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1827 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1828 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1829 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1830 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1831 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1832 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1833 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1834 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1835 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1844 case EL_SPACESHIP_RIGHT:
1845 case EL_SPACESHIP_UP:
1846 case EL_SPACESHIP_LEFT:
1847 case EL_SPACESHIP_DOWN:
1848 case EL_BD_BUTTERFLY:
1849 case EL_BD_BUTTERFLY_RIGHT:
1850 case EL_BD_BUTTERFLY_UP:
1851 case EL_BD_BUTTERFLY_LEFT:
1852 case EL_BD_BUTTERFLY_DOWN:
1854 case EL_BD_FIREFLY_RIGHT:
1855 case EL_BD_FIREFLY_UP:
1856 case EL_BD_FIREFLY_LEFT:
1857 case EL_BD_FIREFLY_DOWN:
1858 case EL_PACMAN_RIGHT:
1860 case EL_PACMAN_LEFT:
1861 case EL_PACMAN_DOWN:
1863 case EL_YAMYAM_LEFT:
1864 case EL_YAMYAM_RIGHT:
1866 case EL_YAMYAM_DOWN:
1867 case EL_DARK_YAMYAM:
1870 case EL_SP_SNIKSNAK:
1871 case EL_SP_ELECTRON:
1880 case EL_AMOEBA_FULL:
1885 case EL_AMOEBA_DROP:
1886 if (y == lev_fieldy - 1)
1888 Feld[x][y] = EL_AMOEBA_GROWING;
1889 Store[x][y] = EL_AMOEBA_WET;
1893 case EL_DYNAMITE_ACTIVE:
1894 case EL_SP_DISK_RED_ACTIVE:
1895 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1896 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1897 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1898 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1899 MovDelay[x][y] = 96;
1902 case EL_EM_DYNAMITE_ACTIVE:
1903 MovDelay[x][y] = 32;
1907 local_player->lights_still_needed++;
1911 local_player->friends_still_needed++;
1916 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1919 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1920 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1921 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1922 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1923 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1924 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1925 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1926 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1927 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1928 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1929 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1930 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1933 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1934 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1935 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1937 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1939 game.belt_dir[belt_nr] = belt_dir;
1940 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1942 else /* more than one switch -- set it like the first switch */
1944 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1949 #if !USE_BOTH_SWITCHGATE_SWITCHES
1950 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1952 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1955 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1957 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1961 case EL_LIGHT_SWITCH_ACTIVE:
1963 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1966 case EL_INVISIBLE_STEELWALL:
1967 case EL_INVISIBLE_WALL:
1968 case EL_INVISIBLE_SAND:
1969 if (game.light_time_left > 0 ||
1970 game.lenses_time_left > 0)
1971 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1974 case EL_EMC_MAGIC_BALL:
1975 if (game.ball_state)
1976 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1979 case EL_EMC_MAGIC_BALL_SWITCH:
1980 if (game.ball_state)
1981 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1984 case EL_TRIGGER_PLAYER:
1985 case EL_TRIGGER_ELEMENT:
1986 case EL_TRIGGER_CE_VALUE:
1987 case EL_TRIGGER_CE_SCORE:
1989 case EL_ANY_ELEMENT:
1990 case EL_CURRENT_CE_VALUE:
1991 case EL_CURRENT_CE_SCORE:
2008 /* reference elements should not be used on the playfield */
2009 Feld[x][y] = EL_EMPTY;
2013 if (IS_CUSTOM_ELEMENT(element))
2015 if (CAN_MOVE(element))
2018 #if USE_NEW_CUSTOM_VALUE
2019 if (!element_info[element].use_last_ce_value || init_game)
2020 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2023 else if (IS_GROUP_ELEMENT(element))
2025 Feld[x][y] = GetElementFromGroupElement(element);
2027 InitField(x, y, init_game);
2034 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2037 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2039 InitField(x, y, init_game);
2041 /* not needed to call InitMovDir() -- already done by InitField()! */
2042 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2043 CAN_MOVE(Feld[x][y]))
2047 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2049 int old_element = Feld[x][y];
2051 InitField(x, y, init_game);
2053 /* not needed to call InitMovDir() -- already done by InitField()! */
2054 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2055 CAN_MOVE(old_element) &&
2056 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2059 /* this case is in fact a combination of not less than three bugs:
2060 first, it calls InitMovDir() for elements that can move, although this is
2061 already done by InitField(); then, it checks the element that was at this
2062 field _before_ the call to InitField() (which can change it); lastly, it
2063 was not called for "mole with direction" elements, which were treated as
2064 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2070 static int get_key_element_from_nr(int key_nr)
2072 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2073 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2074 EL_EM_KEY_1 : EL_KEY_1);
2076 return key_base_element + key_nr;
2079 static int get_next_dropped_element(struct PlayerInfo *player)
2081 return (player->inventory_size > 0 ?
2082 player->inventory_element[player->inventory_size - 1] :
2083 player->inventory_infinite_element != EL_UNDEFINED ?
2084 player->inventory_infinite_element :
2085 player->dynabombs_left > 0 ?
2086 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2090 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2092 /* pos >= 0: get element from bottom of the stack;
2093 pos < 0: get element from top of the stack */
2097 int min_inventory_size = -pos;
2098 int inventory_pos = player->inventory_size - min_inventory_size;
2099 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2101 return (player->inventory_size >= min_inventory_size ?
2102 player->inventory_element[inventory_pos] :
2103 player->inventory_infinite_element != EL_UNDEFINED ?
2104 player->inventory_infinite_element :
2105 player->dynabombs_left >= min_dynabombs_left ?
2106 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2111 int min_dynabombs_left = pos + 1;
2112 int min_inventory_size = pos + 1 - player->dynabombs_left;
2113 int inventory_pos = pos - player->dynabombs_left;
2115 return (player->inventory_infinite_element != EL_UNDEFINED ?
2116 player->inventory_infinite_element :
2117 player->dynabombs_left >= min_dynabombs_left ?
2118 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2119 player->inventory_size >= min_inventory_size ?
2120 player->inventory_element[inventory_pos] :
2125 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2127 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2128 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2131 if (gpo1->sort_priority != gpo2->sort_priority)
2132 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2134 compare_result = gpo1->nr - gpo2->nr;
2136 return compare_result;
2139 void InitGameControlValues()
2143 for (i = 0; game_panel_controls[i].nr != -1; i++)
2145 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2146 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2147 struct TextPosInfo *pos = gpc->pos;
2149 int type = gpc->type;
2153 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2154 Error(ERR_EXIT, "this should not happen -- please debug");
2157 /* force update of game controls after initialization */
2158 gpc->value = gpc->last_value = -1;
2159 gpc->frame = gpc->last_frame = -1;
2160 gpc->gfx_frame = -1;
2162 /* determine panel value width for later calculation of alignment */
2163 if (type == TYPE_INTEGER || type == TYPE_STRING)
2165 pos->width = pos->size * getFontWidth(pos->font);
2166 pos->height = getFontHeight(pos->font);
2168 else if (type == TYPE_ELEMENT)
2170 pos->width = pos->size;
2171 pos->height = pos->size;
2174 /* fill structure for game panel draw order */
2176 gpo->sort_priority = pos->sort_priority;
2179 /* sort game panel controls according to sort_priority and control number */
2180 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2181 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2184 void UpdatePlayfieldElementCount()
2186 boolean use_element_count = FALSE;
2189 /* first check if it is needed at all to calculate playfield element count */
2190 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2191 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2192 use_element_count = TRUE;
2194 if (!use_element_count)
2197 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2198 element_info[i].element_count = 0;
2200 SCAN_PLAYFIELD(x, y)
2202 element_info[Feld[x][y]].element_count++;
2205 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2206 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2207 if (IS_IN_GROUP(j, i))
2208 element_info[EL_GROUP_START + i].element_count +=
2209 element_info[j].element_count;
2212 void UpdateGameControlValues()
2215 int time = (local_player->LevelSolved ?
2216 local_player->LevelSolved_CountingTime :
2217 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2218 level.native_em_level->lev->time :
2219 level.time == 0 ? TimePlayed : TimeLeft);
2220 int score = (local_player->LevelSolved ?
2221 local_player->LevelSolved_CountingScore :
2222 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223 level.native_em_level->lev->score :
2224 local_player->score);
2225 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226 level.native_em_level->lev->required :
2227 local_player->gems_still_needed);
2228 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->required > 0 :
2230 local_player->gems_still_needed > 0 ||
2231 local_player->sokobanfields_still_needed > 0 ||
2232 local_player->lights_still_needed > 0);
2234 UpdatePlayfieldElementCount();
2236 /* update game panel control values */
2238 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2239 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2241 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2242 for (i = 0; i < MAX_NUM_KEYS; i++)
2243 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2244 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2245 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2247 if (game.centered_player_nr == -1)
2249 for (i = 0; i < MAX_PLAYERS; i++)
2251 for (k = 0; k < MAX_NUM_KEYS; k++)
2253 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2255 if (level.native_em_level->ply[i]->keys & (1 << k))
2256 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2257 get_key_element_from_nr(k);
2259 else if (stored_player[i].key[k])
2260 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2261 get_key_element_from_nr(k);
2264 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2265 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2266 level.native_em_level->ply[i]->dynamite;
2268 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2269 stored_player[i].inventory_size;
2271 if (stored_player[i].num_white_keys > 0)
2272 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2275 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2276 stored_player[i].num_white_keys;
2281 int player_nr = game.centered_player_nr;
2283 for (k = 0; k < MAX_NUM_KEYS; k++)
2285 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2288 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2289 get_key_element_from_nr(k);
2291 else if (stored_player[player_nr].key[k])
2292 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2293 get_key_element_from_nr(k);
2296 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2297 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2298 level.native_em_level->ply[player_nr]->dynamite;
2300 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2301 stored_player[player_nr].inventory_size;
2303 if (stored_player[player_nr].num_white_keys > 0)
2304 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2306 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2307 stored_player[player_nr].num_white_keys;
2310 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2312 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2313 get_inventory_element_from_pos(local_player, i);
2314 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2315 get_inventory_element_from_pos(local_player, -i - 1);
2318 game_panel_controls[GAME_PANEL_SCORE].value = score;
2319 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2321 game_panel_controls[GAME_PANEL_TIME].value = time;
2323 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2324 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2325 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2327 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2328 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2330 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2331 local_player->shield_normal_time_left;
2332 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2333 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2335 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2336 local_player->shield_deadly_time_left;
2338 game_panel_controls[GAME_PANEL_EXIT].value =
2339 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2341 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2342 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2343 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2344 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2345 EL_EMC_MAGIC_BALL_SWITCH);
2347 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2348 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2349 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2350 game.light_time_left;
2352 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2353 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2354 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2355 game.timegate_time_left;
2357 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2358 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2360 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2361 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2362 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2363 game.lenses_time_left;
2365 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2366 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2368 game.magnify_time_left;
2370 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2371 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2372 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2373 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2374 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2375 EL_BALLOON_SWITCH_NONE);
2377 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2378 local_player->dynabomb_count;
2379 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2380 local_player->dynabomb_size;
2381 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2382 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2384 game_panel_controls[GAME_PANEL_PENGUINS].value =
2385 local_player->friends_still_needed;
2387 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2388 local_player->sokobanfields_still_needed;
2389 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2390 local_player->sokobanfields_still_needed;
2392 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2393 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2395 for (i = 0; i < NUM_BELTS; i++)
2397 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2398 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2399 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2400 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2401 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2404 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2405 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2406 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2407 game.magic_wall_time_left;
2409 #if USE_PLAYER_GRAVITY
2410 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2411 local_player->gravity;
2413 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2416 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2417 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2419 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2420 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2421 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2422 game.panel.element[i].id : EL_UNDEFINED);
2424 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2426 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2427 element_info[game.panel.element_count[i].id].element_count : 0);
2429 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2430 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2431 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2432 element_info[game.panel.ce_score[i].id].collect_score : 0);
2434 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2436 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2437 element_info[game.panel.ce_score_element[i].id].collect_score :
2440 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2441 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2442 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2444 /* update game panel control frames */
2446 for (i = 0; game_panel_controls[i].nr != -1; i++)
2448 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2450 if (gpc->type == TYPE_ELEMENT)
2452 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2454 int last_anim_random_frame = gfx.anim_random_frame;
2455 int element = gpc->value;
2456 int graphic = el2panelimg(element);
2458 if (gpc->value != gpc->last_value)
2461 gpc->gfx_random = INIT_GFX_RANDOM();
2467 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2468 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2469 gpc->gfx_random = INIT_GFX_RANDOM();
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2473 gfx.anim_random_frame = gpc->gfx_random;
2475 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2476 gpc->gfx_frame = element_info[element].collect_score;
2478 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2481 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2482 gfx.anim_random_frame = last_anim_random_frame;
2488 void DisplayGameControlValues()
2490 boolean redraw_panel = FALSE;
2493 for (i = 0; game_panel_controls[i].nr != -1; i++)
2495 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2497 if (PANEL_DEACTIVATED(gpc->pos))
2500 if (gpc->value == gpc->last_value &&
2501 gpc->frame == gpc->last_frame)
2504 redraw_panel = TRUE;
2510 /* copy default game door content to main double buffer */
2511 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2512 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2514 /* redraw game control buttons */
2516 RedrawGameButtons();
2522 game_status = GAME_MODE_PSEUDO_PANEL;
2525 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2527 for (i = 0; game_panel_controls[i].nr != -1; i++)
2531 int nr = game_panel_order[i].nr;
2532 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2534 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2537 struct TextPosInfo *pos = gpc->pos;
2538 int type = gpc->type;
2539 int value = gpc->value;
2540 int frame = gpc->frame;
2542 int last_value = gpc->last_value;
2543 int last_frame = gpc->last_frame;
2545 int size = pos->size;
2546 int font = pos->font;
2547 boolean draw_masked = pos->draw_masked;
2548 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2550 if (PANEL_DEACTIVATED(pos))
2554 if (value == last_value && frame == last_frame)
2558 gpc->last_value = value;
2559 gpc->last_frame = frame;
2562 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2565 if (type == TYPE_INTEGER)
2567 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2568 nr == GAME_PANEL_TIME)
2570 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2572 if (use_dynamic_size) /* use dynamic number of digits */
2574 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2575 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2576 int size2 = size1 + 1;
2577 int font1 = pos->font;
2578 int font2 = pos->font_alt;
2580 size = (value < value_change ? size1 : size2);
2581 font = (value < value_change ? font1 : font2);
2584 /* clear background if value just changed its size (dynamic digits) */
2585 if ((last_value < value_change) != (value < value_change))
2587 int width1 = size1 * getFontWidth(font1);
2588 int width2 = size2 * getFontWidth(font2);
2589 int max_width = MAX(width1, width2);
2590 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2592 pos->width = max_width;
2594 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2595 max_width, max_height);
2602 /* correct text size if "digits" is zero or less */
2604 size = strlen(int2str(value, size));
2606 /* dynamically correct text alignment */
2607 pos->width = size * getFontWidth(font);
2610 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2611 int2str(value, size), font, mask_mode);
2613 else if (type == TYPE_ELEMENT)
2615 int element, graphic;
2619 int dst_x = PANEL_XPOS(pos);
2620 int dst_y = PANEL_YPOS(pos);
2623 if (value != EL_UNDEFINED && value != EL_EMPTY)
2626 graphic = el2panelimg(value);
2628 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2631 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2635 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2638 width = graphic_info[graphic].width * size / TILESIZE;
2639 height = graphic_info[graphic].height * size / TILESIZE;
2643 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2644 dst_x - src_x, dst_y - src_y);
2645 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2650 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2655 if (value == EL_UNDEFINED || value == EL_EMPTY)
2657 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2658 graphic = el2panelimg(element);
2660 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2661 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2662 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2667 graphic = el2panelimg(value);
2669 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2672 width = graphic_info[graphic].width * size / TILESIZE;
2673 height = graphic_info[graphic].height * size / TILESIZE;
2675 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2678 else if (type == TYPE_STRING)
2680 boolean active = (value != 0);
2681 char *state_normal = "off";
2682 char *state_active = "on";
2683 char *state = (active ? state_active : state_normal);
2684 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2685 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2686 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2687 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2689 if (nr == GAME_PANEL_GRAVITY_STATE)
2691 int font1 = pos->font; /* (used for normal state) */
2692 int font2 = pos->font_alt; /* (used for active state) */
2694 int size1 = strlen(state_normal);
2695 int size2 = strlen(state_active);
2696 int width1 = size1 * getFontWidth(font1);
2697 int width2 = size2 * getFontWidth(font2);
2698 int max_width = MAX(width1, width2);
2699 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2701 pos->width = max_width;
2703 /* clear background for values that may have changed its size */
2704 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2705 max_width, max_height);
2708 font = (active ? font2 : font1);
2718 /* don't truncate output if "chars" is zero or less */
2721 /* dynamically correct text alignment */
2722 pos->width = size * getFontWidth(font);
2726 s_cut = getStringCopyN(s, size);
2728 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2729 s_cut, font, mask_mode);
2735 redraw_mask |= REDRAW_DOOR_1;
2738 game_status = GAME_MODE_PLAYING;
2741 void UpdateAndDisplayGameControlValues()
2743 if (tape.warp_forward)
2746 UpdateGameControlValues();
2747 DisplayGameControlValues();
2750 void DrawGameValue_Emeralds(int value)
2752 struct TextPosInfo *pos = &game.panel.gems;
2754 int font_nr = pos->font;
2756 int font_nr = FONT_TEXT_2;
2758 int font_width = getFontWidth(font_nr);
2759 int chars = pos->size;
2762 return; /* !!! USE NEW STUFF !!! */
2765 if (PANEL_DEACTIVATED(pos))
2768 pos->width = chars * font_width;
2770 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2773 void DrawGameValue_Dynamite(int value)
2775 struct TextPosInfo *pos = &game.panel.inventory_count;
2777 int font_nr = pos->font;
2779 int font_nr = FONT_TEXT_2;
2781 int font_width = getFontWidth(font_nr);
2782 int chars = pos->size;
2785 return; /* !!! USE NEW STUFF !!! */
2788 if (PANEL_DEACTIVATED(pos))
2791 pos->width = chars * font_width;
2793 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2796 void DrawGameValue_Score(int value)
2798 struct TextPosInfo *pos = &game.panel.score;
2800 int font_nr = pos->font;
2802 int font_nr = FONT_TEXT_2;
2804 int font_width = getFontWidth(font_nr);
2805 int chars = pos->size;
2808 return; /* !!! USE NEW STUFF !!! */
2811 if (PANEL_DEACTIVATED(pos))
2814 pos->width = chars * font_width;
2816 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2819 void DrawGameValue_Time(int value)
2821 struct TextPosInfo *pos = &game.panel.time;
2822 static int last_value = -1;
2825 int chars = pos->size;
2827 int font1_nr = pos->font;
2828 int font2_nr = pos->font_alt;
2830 int font1_nr = FONT_TEXT_2;
2831 int font2_nr = FONT_TEXT_1;
2833 int font_nr = font1_nr;
2834 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2837 return; /* !!! USE NEW STUFF !!! */
2840 if (PANEL_DEACTIVATED(pos))
2843 if (use_dynamic_chars) /* use dynamic number of chars */
2845 chars = (value < 1000 ? chars1 : chars2);
2846 font_nr = (value < 1000 ? font1_nr : font2_nr);
2849 /* clear background if value just changed its size (dynamic chars only) */
2850 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2852 int width1 = chars1 * getFontWidth(font1_nr);
2853 int width2 = chars2 * getFontWidth(font2_nr);
2854 int max_width = MAX(width1, width2);
2855 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2857 pos->width = max_width;
2859 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2860 max_width, max_height);
2863 pos->width = chars * getFontWidth(font_nr);
2865 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2870 void DrawGameValue_Level(int value)
2872 struct TextPosInfo *pos = &game.panel.level_number;
2875 int chars = pos->size;
2877 int font1_nr = pos->font;
2878 int font2_nr = pos->font_alt;
2880 int font1_nr = FONT_TEXT_2;
2881 int font2_nr = FONT_TEXT_1;
2883 int font_nr = font1_nr;
2884 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2887 return; /* !!! USE NEW STUFF !!! */
2890 if (PANEL_DEACTIVATED(pos))
2893 if (use_dynamic_chars) /* use dynamic number of chars */
2895 chars = (level_nr < 100 ? chars1 : chars2);
2896 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2899 pos->width = chars * getFontWidth(font_nr);
2901 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2904 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2907 struct TextPosInfo *pos = &game.panel.keys;
2910 int base_key_graphic = EL_KEY_1;
2915 return; /* !!! USE NEW STUFF !!! */
2919 if (PANEL_DEACTIVATED(pos))
2924 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2925 base_key_graphic = EL_EM_KEY_1;
2929 pos->width = 4 * MINI_TILEX;
2933 for (i = 0; i < MAX_NUM_KEYS; i++)
2935 /* currently only 4 of 8 possible keys are displayed */
2936 for (i = 0; i < STD_NUM_KEYS; i++)
2940 struct TextPosInfo *pos = &game.panel.key[i];
2942 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2943 int src_y = DOOR_GFX_PAGEY1 + 123;
2945 int dst_x = PANEL_XPOS(pos);
2946 int dst_y = PANEL_YPOS(pos);
2948 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2949 int dst_y = PANEL_YPOS(pos);
2953 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2954 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2956 int graphic = el2edimg(element);
2960 if (PANEL_DEACTIVATED(pos))
2965 /* masked blit with tiles from half-size scaled bitmap does not work yet
2966 (no mask bitmap created for these sizes after loading and scaling) --
2967 solution: load without creating mask, scale, then create final mask */
2969 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2970 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2975 int graphic = el2edimg(base_key_graphic + i);
2980 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2982 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2983 dst_x - src_x, dst_y - src_y);
2984 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2990 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2992 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2993 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2996 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2998 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2999 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3007 void DrawGameValue_Emeralds(int value)
3009 int font_nr = FONT_TEXT_2;
3010 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3012 if (PANEL_DEACTIVATED(game.panel.gems))
3015 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3018 void DrawGameValue_Dynamite(int value)
3020 int font_nr = FONT_TEXT_2;
3021 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3023 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3026 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3029 void DrawGameValue_Score(int value)
3031 int font_nr = FONT_TEXT_2;
3032 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3034 if (PANEL_DEACTIVATED(game.panel.score))
3037 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3040 void DrawGameValue_Time(int value)
3042 int font1_nr = FONT_TEXT_2;
3044 int font2_nr = FONT_TEXT_1;
3046 int font2_nr = FONT_LEVEL_NUMBER;
3048 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3049 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3051 if (PANEL_DEACTIVATED(game.panel.time))
3054 /* clear background if value just changed its size */
3055 if (value == 999 || value == 1000)
3056 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3059 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3061 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3064 void DrawGameValue_Level(int value)
3066 int font1_nr = FONT_TEXT_2;
3068 int font2_nr = FONT_TEXT_1;
3070 int font2_nr = FONT_LEVEL_NUMBER;
3073 if (PANEL_DEACTIVATED(game.panel.level))
3077 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3079 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3082 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3084 int base_key_graphic = EL_KEY_1;
3087 if (PANEL_DEACTIVATED(game.panel.keys))
3090 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3091 base_key_graphic = EL_EM_KEY_1;
3093 /* currently only 4 of 8 possible keys are displayed */
3094 for (i = 0; i < STD_NUM_KEYS; i++)
3096 int x = XX_KEYS + i * MINI_TILEX;
3100 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3102 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3103 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3109 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3112 int key[MAX_NUM_KEYS];
3115 /* prevent EM engine from updating time/score values parallel to GameWon() */
3116 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3117 local_player->LevelSolved)
3120 for (i = 0; i < MAX_NUM_KEYS; i++)
3121 key[i] = key_bits & (1 << i);
3123 DrawGameValue_Level(level_nr);
3125 DrawGameValue_Emeralds(emeralds);
3126 DrawGameValue_Dynamite(dynamite);
3127 DrawGameValue_Score(score);
3128 DrawGameValue_Time(time);
3130 DrawGameValue_Keys(key);
3133 void UpdateGameDoorValues()
3135 UpdateGameControlValues();
3138 void DrawGameDoorValues()
3140 DisplayGameControlValues();
3143 void DrawGameDoorValues_OLD()
3145 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3146 int dynamite_value = 0;
3147 int score_value = (local_player->LevelSolved ? local_player->score_final :
3148 local_player->score);
3149 int gems_value = local_player->gems_still_needed;
3153 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3155 DrawGameDoorValues_EM();
3160 if (game.centered_player_nr == -1)
3162 for (i = 0; i < MAX_PLAYERS; i++)
3164 for (j = 0; j < MAX_NUM_KEYS; j++)
3165 if (stored_player[i].key[j])
3166 key_bits |= (1 << j);
3168 dynamite_value += stored_player[i].inventory_size;
3173 int player_nr = game.centered_player_nr;
3175 for (i = 0; i < MAX_NUM_KEYS; i++)
3176 if (stored_player[player_nr].key[i])
3177 key_bits |= (1 << i);
3179 dynamite_value = stored_player[player_nr].inventory_size;
3182 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3188 =============================================================================
3190 -----------------------------------------------------------------------------
3191 initialize game engine due to level / tape version number
3192 =============================================================================
3195 static void InitGameEngine()
3197 int i, j, k, l, x, y;
3199 /* set game engine from tape file when re-playing, else from level file */
3200 game.engine_version = (tape.playing ? tape.engine_version :
3201 level.game_version);
3203 /* ---------------------------------------------------------------------- */
3204 /* set flags for bugs and changes according to active game engine version */
3205 /* ---------------------------------------------------------------------- */
3208 Summary of bugfix/change:
3209 Fixed handling for custom elements that change when pushed by the player.
3211 Fixed/changed in version:
3215 Before 3.1.0, custom elements that "change when pushing" changed directly
3216 after the player started pushing them (until then handled in "DigField()").
3217 Since 3.1.0, these custom elements are not changed until the "pushing"
3218 move of the element is finished (now handled in "ContinueMoving()").
3220 Affected levels/tapes:
3221 The first condition is generally needed for all levels/tapes before version
3222 3.1.0, which might use the old behaviour before it was changed; known tapes
3223 that are affected are some tapes from the level set "Walpurgis Gardens" by
3225 The second condition is an exception from the above case and is needed for
3226 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3227 above (including some development versions of 3.1.0), but before it was
3228 known that this change would break tapes like the above and was fixed in
3229 3.1.1, so that the changed behaviour was active although the engine version
3230 while recording maybe was before 3.1.0. There is at least one tape that is
3231 affected by this exception, which is the tape for the one-level set "Bug
3232 Machine" by Juergen Bonhagen.
3235 game.use_change_when_pushing_bug =
3236 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3238 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3239 tape.game_version < VERSION_IDENT(3,1,1,0)));
3242 Summary of bugfix/change:
3243 Fixed handling for blocking the field the player leaves when moving.
3245 Fixed/changed in version:
3249 Before 3.1.1, when "block last field when moving" was enabled, the field
3250 the player is leaving when moving was blocked for the time of the move,
3251 and was directly unblocked afterwards. This resulted in the last field
3252 being blocked for exactly one less than the number of frames of one player
3253 move. Additionally, even when blocking was disabled, the last field was
3254 blocked for exactly one frame.
3255 Since 3.1.1, due to changes in player movement handling, the last field
3256 is not blocked at all when blocking is disabled. When blocking is enabled,
3257 the last field is blocked for exactly the number of frames of one player
3258 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3259 last field is blocked for exactly one more than the number of frames of
3262 Affected levels/tapes:
3263 (!!! yet to be determined -- probably many !!!)
3266 game.use_block_last_field_bug =
3267 (game.engine_version < VERSION_IDENT(3,1,1,0));
3270 Summary of bugfix/change:
3271 Changed behaviour of CE changes with multiple changes per single frame.
3273 Fixed/changed in version:
3277 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3278 This resulted in race conditions where CEs seem to behave strange in some
3279 situations (where triggered CE changes were just skipped because there was
3280 already a CE change on that tile in the playfield in that engine frame).
3281 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3282 (The number of changes per frame must be limited in any case, because else
3283 it is easily possible to define CE changes that would result in an infinite
3284 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3285 should be set large enough so that it would only be reached in cases where
3286 the corresponding CE change conditions run into a loop. Therefore, it seems
3287 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3288 maximal number of change pages for custom elements.)
3290 Affected levels/tapes:
3294 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3295 game.max_num_changes_per_frame = 1;
3297 game.max_num_changes_per_frame =
3298 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3301 /* ---------------------------------------------------------------------- */
3303 /* default scan direction: scan playfield from top/left to bottom/right */
3304 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3306 /* dynamically adjust element properties according to game engine version */
3307 InitElementPropertiesEngine(game.engine_version);
3310 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3311 printf(" tape version == %06d [%s] [file: %06d]\n",
3312 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3314 printf(" => game.engine_version == %06d\n", game.engine_version);
3317 /* ---------- initialize player's initial move delay --------------------- */
3319 /* dynamically adjust player properties according to level information */
3320 for (i = 0; i < MAX_PLAYERS; i++)
3321 game.initial_move_delay_value[i] =
3322 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3324 /* dynamically adjust player properties according to game engine version */
3325 for (i = 0; i < MAX_PLAYERS; i++)
3326 game.initial_move_delay[i] =
3327 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3328 game.initial_move_delay_value[i] : 0);
3330 /* ---------- initialize player's initial push delay --------------------- */
3332 /* dynamically adjust player properties according to game engine version */
3333 game.initial_push_delay_value =
3334 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3336 /* ---------- initialize changing elements ------------------------------- */
3338 /* initialize changing elements information */
3339 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3341 struct ElementInfo *ei = &element_info[i];
3343 /* this pointer might have been changed in the level editor */
3344 ei->change = &ei->change_page[0];
3346 if (!IS_CUSTOM_ELEMENT(i))
3348 ei->change->target_element = EL_EMPTY_SPACE;
3349 ei->change->delay_fixed = 0;
3350 ei->change->delay_random = 0;
3351 ei->change->delay_frames = 1;
3354 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3356 ei->has_change_event[j] = FALSE;
3358 ei->event_page_nr[j] = 0;
3359 ei->event_page[j] = &ei->change_page[0];
3363 /* add changing elements from pre-defined list */
3364 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3366 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3367 struct ElementInfo *ei = &element_info[ch_delay->element];
3369 ei->change->target_element = ch_delay->target_element;
3370 ei->change->delay_fixed = ch_delay->change_delay;
3372 ei->change->pre_change_function = ch_delay->pre_change_function;
3373 ei->change->change_function = ch_delay->change_function;
3374 ei->change->post_change_function = ch_delay->post_change_function;
3376 ei->change->can_change = TRUE;
3377 ei->change->can_change_or_has_action = TRUE;
3379 ei->has_change_event[CE_DELAY] = TRUE;
3381 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3382 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3385 /* ---------- initialize internal run-time variables --------------------- */
3387 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3389 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3391 for (j = 0; j < ei->num_change_pages; j++)
3393 ei->change_page[j].can_change_or_has_action =
3394 (ei->change_page[j].can_change |
3395 ei->change_page[j].has_action);
3399 /* add change events from custom element configuration */
3400 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3402 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3404 for (j = 0; j < ei->num_change_pages; j++)
3406 if (!ei->change_page[j].can_change_or_has_action)
3409 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3411 /* only add event page for the first page found with this event */
3412 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3414 ei->has_change_event[k] = TRUE;
3416 ei->event_page_nr[k] = j;
3417 ei->event_page[k] = &ei->change_page[j];
3424 /* ---------- initialize reference elements in change conditions --------- */
3426 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3428 int element = EL_CUSTOM_START + i;
3429 struct ElementInfo *ei = &element_info[element];
3431 for (j = 0; j < ei->num_change_pages; j++)
3433 int trigger_element = ei->change_page[j].initial_trigger_element;
3435 if (trigger_element >= EL_PREV_CE_8 &&
3436 trigger_element <= EL_NEXT_CE_8)
3437 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3439 ei->change_page[j].trigger_element = trigger_element;
3444 /* ---------- initialize run-time trigger player and element ------------- */
3446 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3448 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3450 for (j = 0; j < ei->num_change_pages; j++)
3452 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3453 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3454 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3455 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3456 ei->change_page[j].actual_trigger_ce_value = 0;
3457 ei->change_page[j].actual_trigger_ce_score = 0;
3461 /* ---------- initialize trigger events ---------------------------------- */
3463 /* initialize trigger events information */
3464 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3466 trigger_events[i][j] = FALSE;
3468 /* add trigger events from element change event properties */
3469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471 struct ElementInfo *ei = &element_info[i];
3473 for (j = 0; j < ei->num_change_pages; j++)
3475 if (!ei->change_page[j].can_change_or_has_action)
3478 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3480 int trigger_element = ei->change_page[j].trigger_element;
3482 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3484 if (ei->change_page[j].has_event[k])
3486 if (IS_GROUP_ELEMENT(trigger_element))
3488 struct ElementGroupInfo *group =
3489 element_info[trigger_element].group;
3491 for (l = 0; l < group->num_elements_resolved; l++)
3492 trigger_events[group->element_resolved[l]][k] = TRUE;
3494 else if (trigger_element == EL_ANY_ELEMENT)
3495 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3496 trigger_events[l][k] = TRUE;
3498 trigger_events[trigger_element][k] = TRUE;
3505 /* ---------- initialize push delay -------------------------------------- */
3507 /* initialize push delay values to default */
3508 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3510 if (!IS_CUSTOM_ELEMENT(i))
3512 /* set default push delay values (corrected since version 3.0.7-1) */
3513 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3515 element_info[i].push_delay_fixed = 2;
3516 element_info[i].push_delay_random = 8;
3520 element_info[i].push_delay_fixed = 8;
3521 element_info[i].push_delay_random = 8;
3526 /* set push delay value for certain elements from pre-defined list */
3527 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3529 int e = push_delay_list[i].element;
3531 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3532 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3535 /* set push delay value for Supaplex elements for newer engine versions */
3536 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3538 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540 if (IS_SP_ELEMENT(i))
3542 /* set SP push delay to just enough to push under a falling zonk */
3543 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3545 element_info[i].push_delay_fixed = delay;
3546 element_info[i].push_delay_random = 0;
3551 /* ---------- initialize move stepsize ----------------------------------- */
3553 /* initialize move stepsize values to default */
3554 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3555 if (!IS_CUSTOM_ELEMENT(i))
3556 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3558 /* set move stepsize value for certain elements from pre-defined list */
3559 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3561 int e = move_stepsize_list[i].element;
3563 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3566 /* ---------- initialize collect score ----------------------------------- */
3568 /* initialize collect score values for custom elements from initial value */
3569 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570 if (IS_CUSTOM_ELEMENT(i))
3571 element_info[i].collect_score = element_info[i].collect_score_initial;
3573 /* ---------- initialize collect count ----------------------------------- */
3575 /* initialize collect count values for non-custom elements */
3576 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3577 if (!IS_CUSTOM_ELEMENT(i))
3578 element_info[i].collect_count_initial = 0;
3580 /* add collect count values for all elements from pre-defined list */
3581 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3582 element_info[collect_count_list[i].element].collect_count_initial =
3583 collect_count_list[i].count;
3585 /* ---------- initialize access direction -------------------------------- */
3587 /* initialize access direction values to default (access from every side) */
3588 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3589 if (!IS_CUSTOM_ELEMENT(i))
3590 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3592 /* set access direction value for certain elements from pre-defined list */
3593 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3594 element_info[access_direction_list[i].element].access_direction =
3595 access_direction_list[i].direction;
3597 /* ---------- initialize explosion content ------------------------------- */
3598 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3600 if (IS_CUSTOM_ELEMENT(i))
3603 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3605 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3607 element_info[i].content.e[x][y] =
3608 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3609 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3610 i == EL_PLAYER_3 ? EL_EMERALD :
3611 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3612 i == EL_MOLE ? EL_EMERALD_RED :
3613 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3614 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3615 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3616 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3617 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3618 i == EL_WALL_EMERALD ? EL_EMERALD :
3619 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3620 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3621 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3622 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3623 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3624 i == EL_WALL_PEARL ? EL_PEARL :
3625 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3630 /* ---------- initialize recursion detection ------------------------------ */
3631 recursion_loop_depth = 0;
3632 recursion_loop_detected = FALSE;
3633 recursion_loop_element = EL_UNDEFINED;
3635 /* ---------- initialize graphics engine ---------------------------------- */
3636 game.scroll_delay_value =
3637 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3638 setup.scroll_delay ? setup.scroll_delay_value : 0);
3639 game.scroll_delay_value =
3640 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3643 int get_num_special_action(int element, int action_first, int action_last)
3645 int num_special_action = 0;
3648 for (i = action_first; i <= action_last; i++)
3650 boolean found = FALSE;
3652 for (j = 0; j < NUM_DIRECTIONS; j++)
3653 if (el_act_dir2img(element, i, j) !=
3654 el_act_dir2img(element, ACTION_DEFAULT, j))
3658 num_special_action++;
3663 return num_special_action;
3668 =============================================================================
3670 -----------------------------------------------------------------------------
3671 initialize and start new game
3672 =============================================================================
3677 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3678 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3679 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3681 boolean do_fading = (game_status == GAME_MODE_MAIN);
3685 game_status = GAME_MODE_PLAYING;
3688 InitGameControlValues();
3690 /* don't play tapes over network */
3691 network_playing = (options.network && !tape.playing);
3693 for (i = 0; i < MAX_PLAYERS; i++)
3695 struct PlayerInfo *player = &stored_player[i];
3697 player->index_nr = i;
3698 player->index_bit = (1 << i);
3699 player->element_nr = EL_PLAYER_1 + i;
3701 player->present = FALSE;
3702 player->active = FALSE;
3703 player->killed = FALSE;
3706 player->effective_action = 0;
3707 player->programmed_action = 0;
3710 player->score_final = 0;
3712 player->gems_still_needed = level.gems_needed;
3713 player->sokobanfields_still_needed = 0;
3714 player->lights_still_needed = 0;
3715 player->friends_still_needed = 0;
3717 for (j = 0; j < MAX_NUM_KEYS; j++)
3718 player->key[j] = FALSE;
3720 player->num_white_keys = 0;
3722 player->dynabomb_count = 0;
3723 player->dynabomb_size = 1;
3724 player->dynabombs_left = 0;
3725 player->dynabomb_xl = FALSE;
3727 player->MovDir = MV_NONE;
3730 player->GfxDir = MV_NONE;
3731 player->GfxAction = ACTION_DEFAULT;
3733 player->StepFrame = 0;
3735 player->initial_element = player->element_nr;
3736 player->artwork_element =
3737 (level.use_artwork_element[i] ? level.artwork_element[i] :
3738 player->element_nr);
3739 player->use_murphy = FALSE;
3741 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3742 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3744 player->gravity = level.initial_player_gravity[i];
3746 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3748 player->actual_frame_counter = 0;
3750 player->step_counter = 0;
3752 player->last_move_dir = MV_NONE;
3754 player->is_active = FALSE;
3756 player->is_waiting = FALSE;
3757 player->is_moving = FALSE;
3758 player->is_auto_moving = FALSE;
3759 player->is_digging = FALSE;
3760 player->is_snapping = FALSE;
3761 player->is_collecting = FALSE;
3762 player->is_pushing = FALSE;
3763 player->is_switching = FALSE;
3764 player->is_dropping = FALSE;
3765 player->is_dropping_pressed = FALSE;
3767 player->is_bored = FALSE;
3768 player->is_sleeping = FALSE;
3770 player->frame_counter_bored = -1;
3771 player->frame_counter_sleeping = -1;
3773 player->anim_delay_counter = 0;
3774 player->post_delay_counter = 0;
3776 player->dir_waiting = MV_NONE;
3777 player->action_waiting = ACTION_DEFAULT;
3778 player->last_action_waiting = ACTION_DEFAULT;
3779 player->special_action_bored = ACTION_DEFAULT;
3780 player->special_action_sleeping = ACTION_DEFAULT;
3782 player->switch_x = -1;
3783 player->switch_y = -1;
3785 player->drop_x = -1;
3786 player->drop_y = -1;
3788 player->show_envelope = 0;
3790 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3792 player->push_delay = -1; /* initialized when pushing starts */
3793 player->push_delay_value = game.initial_push_delay_value;
3795 player->drop_delay = 0;
3796 player->drop_pressed_delay = 0;
3798 player->last_jx = -1;
3799 player->last_jy = -1;
3803 player->shield_normal_time_left = 0;
3804 player->shield_deadly_time_left = 0;
3806 player->inventory_infinite_element = EL_UNDEFINED;
3807 player->inventory_size = 0;
3809 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3810 SnapField(player, 0, 0);
3812 player->LevelSolved = FALSE;
3813 player->GameOver = FALSE;
3815 player->LevelSolved_GameWon = FALSE;
3816 player->LevelSolved_GameEnd = FALSE;
3817 player->LevelSolved_PanelOff = FALSE;
3818 player->LevelSolved_SaveTape = FALSE;
3819 player->LevelSolved_SaveScore = FALSE;
3820 player->LevelSolved_CountingTime = 0;
3821 player->LevelSolved_CountingScore = 0;
3824 network_player_action_received = FALSE;
3826 #if defined(NETWORK_AVALIABLE)
3827 /* initial null action */
3828 if (network_playing)
3829 SendToServer_MovePlayer(MV_NONE);
3838 TimeLeft = level.time;
3841 ScreenMovDir = MV_NONE;
3845 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3847 AllPlayersGone = FALSE;
3849 game.yamyam_content_nr = 0;
3850 game.robot_wheel_active = FALSE;
3851 game.magic_wall_active = FALSE;
3852 game.magic_wall_time_left = 0;
3853 game.light_time_left = 0;
3854 game.timegate_time_left = 0;
3855 game.switchgate_pos = 0;
3856 game.wind_direction = level.wind_direction_initial;
3858 #if !USE_PLAYER_GRAVITY
3859 game.gravity = FALSE;
3860 game.explosions_delayed = TRUE;
3863 game.lenses_time_left = 0;
3864 game.magnify_time_left = 0;
3866 game.ball_state = level.ball_state_initial;
3867 game.ball_content_nr = 0;
3869 game.envelope_active = FALSE;
3871 /* set focus to local player for network games, else to all players */
3872 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3873 game.centered_player_nr_next = game.centered_player_nr;
3874 game.set_centered_player = FALSE;
3876 if (network_playing && tape.recording)
3878 /* store client dependent player focus when recording network games */
3879 tape.centered_player_nr_next = game.centered_player_nr_next;
3880 tape.set_centered_player = TRUE;
3883 for (i = 0; i < NUM_BELTS; i++)
3885 game.belt_dir[i] = MV_NONE;
3886 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3889 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3890 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3892 SCAN_PLAYFIELD(x, y)
3894 Feld[x][y] = level.field[x][y];
3895 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3896 ChangeDelay[x][y] = 0;
3897 ChangePage[x][y] = -1;
3898 #if USE_NEW_CUSTOM_VALUE
3899 CustomValue[x][y] = 0; /* initialized in InitField() */
3901 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3903 WasJustMoving[x][y] = 0;
3904 WasJustFalling[x][y] = 0;
3905 CheckCollision[x][y] = 0;
3906 CheckImpact[x][y] = 0;
3908 Pushed[x][y] = FALSE;
3910 ChangeCount[x][y] = 0;
3911 ChangeEvent[x][y] = -1;
3913 ExplodePhase[x][y] = 0;
3914 ExplodeDelay[x][y] = 0;
3915 ExplodeField[x][y] = EX_TYPE_NONE;
3917 RunnerVisit[x][y] = 0;
3918 PlayerVisit[x][y] = 0;
3921 GfxRandom[x][y] = INIT_GFX_RANDOM();
3922 GfxElement[x][y] = EL_UNDEFINED;
3923 GfxAction[x][y] = ACTION_DEFAULT;
3924 GfxDir[x][y] = MV_NONE;
3925 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3928 SCAN_PLAYFIELD(x, y)
3930 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3932 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3934 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3937 InitField(x, y, TRUE);
3939 ResetGfxAnimation(x, y);
3944 for (i = 0; i < MAX_PLAYERS; i++)
3946 struct PlayerInfo *player = &stored_player[i];
3948 /* set number of special actions for bored and sleeping animation */
3949 player->num_special_action_bored =
3950 get_num_special_action(player->artwork_element,
3951 ACTION_BORING_1, ACTION_BORING_LAST);
3952 player->num_special_action_sleeping =
3953 get_num_special_action(player->artwork_element,
3954 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3957 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3958 emulate_sb ? EMU_SOKOBAN :
3959 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3961 #if USE_NEW_ALL_SLIPPERY
3962 /* initialize type of slippery elements */
3963 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3965 if (!IS_CUSTOM_ELEMENT(i))
3967 /* default: elements slip down either to the left or right randomly */
3968 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3970 /* SP style elements prefer to slip down on the left side */
3971 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3972 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3974 /* BD style elements prefer to slip down on the left side */
3975 if (game.emulation == EMU_BOULDERDASH)
3976 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3981 /* initialize explosion and ignition delay */
3982 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3984 if (!IS_CUSTOM_ELEMENT(i))
3987 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3988 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3989 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3990 int last_phase = (num_phase + 1) * delay;
3991 int half_phase = (num_phase / 2) * delay;
3993 element_info[i].explosion_delay = last_phase - 1;
3994 element_info[i].ignition_delay = half_phase;
3996 if (i == EL_BLACK_ORB)
3997 element_info[i].ignition_delay = 1;
4001 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4002 element_info[i].explosion_delay = 1;
4004 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4005 element_info[i].ignition_delay = 1;
4009 /* correct non-moving belts to start moving left */
4010 for (i = 0; i < NUM_BELTS; i++)
4011 if (game.belt_dir[i] == MV_NONE)
4012 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4014 /* check if any connected player was not found in playfield */
4015 for (i = 0; i < MAX_PLAYERS; i++)
4017 struct PlayerInfo *player = &stored_player[i];
4019 if (player->connected && !player->present)
4021 for (j = 0; j < MAX_PLAYERS; j++)
4023 struct PlayerInfo *some_player = &stored_player[j];
4024 int jx = some_player->jx, jy = some_player->jy;
4026 /* assign first free player found that is present in the playfield */
4027 if (some_player->present && !some_player->connected)
4029 player->present = TRUE;
4030 player->active = TRUE;
4032 some_player->present = FALSE;
4033 some_player->active = FALSE;
4035 player->initial_element = some_player->initial_element;
4036 player->artwork_element = some_player->artwork_element;
4038 player->block_last_field = some_player->block_last_field;
4039 player->block_delay_adjustment = some_player->block_delay_adjustment;
4041 StorePlayer[jx][jy] = player->element_nr;
4042 player->jx = player->last_jx = jx;
4043 player->jy = player->last_jy = jy;
4053 /* when playing a tape, eliminate all players who do not participate */
4055 for (i = 0; i < MAX_PLAYERS; i++)
4057 if (stored_player[i].active && !tape.player_participates[i])
4059 struct PlayerInfo *player = &stored_player[i];
4060 int jx = player->jx, jy = player->jy;
4062 player->active = FALSE;
4063 StorePlayer[jx][jy] = 0;
4064 Feld[jx][jy] = EL_EMPTY;
4068 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4070 /* when in single player mode, eliminate all but the first active player */
4072 for (i = 0; i < MAX_PLAYERS; i++)
4074 if (stored_player[i].active)
4076 for (j = i + 1; j < MAX_PLAYERS; j++)
4078 if (stored_player[j].active)
4080 struct PlayerInfo *player = &stored_player[j];
4081 int jx = player->jx, jy = player->jy;
4083 player->active = FALSE;
4084 player->present = FALSE;
4086 StorePlayer[jx][jy] = 0;
4087 Feld[jx][jy] = EL_EMPTY;
4094 /* when recording the game, store which players take part in the game */
4097 for (i = 0; i < MAX_PLAYERS; i++)
4098 if (stored_player[i].active)
4099 tape.player_participates[i] = TRUE;
4104 for (i = 0; i < MAX_PLAYERS; i++)
4106 struct PlayerInfo *player = &stored_player[i];
4108 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4113 if (local_player == player)
4114 printf("Player %d is local player.\n", i+1);
4118 if (BorderElement == EL_EMPTY)
4121 SBX_Right = lev_fieldx - SCR_FIELDX;
4123 SBY_Lower = lev_fieldy - SCR_FIELDY;
4128 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4130 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4133 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4134 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4136 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4137 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4139 /* if local player not found, look for custom element that might create
4140 the player (make some assumptions about the right custom element) */
4141 if (!local_player->present)
4143 int start_x = 0, start_y = 0;
4144 int found_rating = 0;
4145 int found_element = EL_UNDEFINED;
4146 int player_nr = local_player->index_nr;
4148 SCAN_PLAYFIELD(x, y)
4150 int element = Feld[x][y];
4155 if (level.use_start_element[player_nr] &&
4156 level.start_element[player_nr] == element &&
4163 found_element = element;
4166 if (!IS_CUSTOM_ELEMENT(element))
4169 if (CAN_CHANGE(element))
4171 for (i = 0; i < element_info[element].num_change_pages; i++)
4173 /* check for player created from custom element as single target */
4174 content = element_info[element].change_page[i].target_element;
4175 is_player = ELEM_IS_PLAYER(content);
4177 if (is_player && (found_rating < 3 ||
4178 (found_rating == 3 && element < found_element)))
4184 found_element = element;
4189 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4191 /* check for player created from custom element as explosion content */
4192 content = element_info[element].content.e[xx][yy];
4193 is_player = ELEM_IS_PLAYER(content);
4195 if (is_player && (found_rating < 2 ||
4196 (found_rating == 2 && element < found_element)))
4198 start_x = x + xx - 1;
4199 start_y = y + yy - 1;
4202 found_element = element;
4205 if (!CAN_CHANGE(element))
4208 for (i = 0; i < element_info[element].num_change_pages; i++)
4210 /* check for player created from custom element as extended target */
4212 element_info[element].change_page[i].target_content.e[xx][yy];
4214 is_player = ELEM_IS_PLAYER(content);
4216 if (is_player && (found_rating < 1 ||
4217 (found_rating == 1 && element < found_element)))
4219 start_x = x + xx - 1;
4220 start_y = y + yy - 1;
4223 found_element = element;
4229 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4230 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4233 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4234 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4239 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4240 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4241 local_player->jx - MIDPOSX);
4243 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4244 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4245 local_player->jy - MIDPOSY);
4249 /* do not use PLAYING mask for fading out from main screen */
4250 game_status = GAME_MODE_MAIN;
4255 if (!game.restart_level)
4256 CloseDoor(DOOR_CLOSE_1);
4259 if (level_editor_test_game)
4260 FadeSkipNextFadeIn();
4262 FadeSetEnterScreen();
4264 if (level_editor_test_game)
4265 fading = fading_none;
4267 fading = menu.destination;
4271 FadeOut(REDRAW_FIELD);
4274 FadeOut(REDRAW_FIELD);
4278 game_status = GAME_MODE_PLAYING;
4281 /* !!! FIX THIS (START) !!! */
4282 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4284 InitGameEngine_EM();
4286 /* blit playfield from scroll buffer to normal back buffer for fading in */
4287 BlitScreenToBitmap_EM(backbuffer);
4294 /* after drawing the level, correct some elements */
4295 if (game.timegate_time_left == 0)
4296 CloseAllOpenTimegates();
4298 /* blit playfield from scroll buffer to normal back buffer for fading in */
4299 if (setup.soft_scrolling)
4300 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4302 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4304 /* !!! FIX THIS (END) !!! */
4307 FadeIn(REDRAW_FIELD);
4310 FadeIn(REDRAW_FIELD);
4315 if (!game.restart_level)
4317 /* copy default game door content to main double buffer */
4318 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4319 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4322 SetPanelBackground();
4323 SetDrawBackgroundMask(REDRAW_DOOR_1);
4326 UpdateAndDisplayGameControlValues();
4328 UpdateGameDoorValues();
4329 DrawGameDoorValues();
4332 if (!game.restart_level)
4336 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4337 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4338 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4342 /* copy actual game door content to door double buffer for OpenDoor() */
4343 BlitBitmap(drawto, bitmap_db_door,
4344 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4346 OpenDoor(DOOR_OPEN_ALL);
4348 PlaySound(SND_GAME_STARTING);
4350 if (setup.sound_music)
4353 KeyboardAutoRepeatOffUnlessAutoplay();
4357 for (i = 0; i < MAX_PLAYERS; i++)
4358 printf("Player %d %sactive.\n",
4359 i + 1, (stored_player[i].active ? "" : "not "));
4370 game.restart_level = FALSE;
4373 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4375 /* this is used for non-R'n'D game engines to update certain engine values */
4377 /* needed to determine if sounds are played within the visible screen area */
4378 scroll_x = actual_scroll_x;
4379 scroll_y = actual_scroll_y;
4382 void InitMovDir(int x, int y)
4384 int i, element = Feld[x][y];
4385 static int xy[4][2] =
4392 static int direction[3][4] =
4394 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4395 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4396 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4405 Feld[x][y] = EL_BUG;
4406 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4409 case EL_SPACESHIP_RIGHT:
4410 case EL_SPACESHIP_UP:
4411 case EL_SPACESHIP_LEFT:
4412 case EL_SPACESHIP_DOWN:
4413 Feld[x][y] = EL_SPACESHIP;
4414 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4417 case EL_BD_BUTTERFLY_RIGHT:
4418 case EL_BD_BUTTERFLY_UP:
4419 case EL_BD_BUTTERFLY_LEFT:
4420 case EL_BD_BUTTERFLY_DOWN:
4421 Feld[x][y] = EL_BD_BUTTERFLY;
4422 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4425 case EL_BD_FIREFLY_RIGHT:
4426 case EL_BD_FIREFLY_UP:
4427 case EL_BD_FIREFLY_LEFT:
4428 case EL_BD_FIREFLY_DOWN:
4429 Feld[x][y] = EL_BD_FIREFLY;
4430 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4433 case EL_PACMAN_RIGHT:
4435 case EL_PACMAN_LEFT:
4436 case EL_PACMAN_DOWN:
4437 Feld[x][y] = EL_PACMAN;
4438 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4441 case EL_YAMYAM_LEFT:
4442 case EL_YAMYAM_RIGHT:
4444 case EL_YAMYAM_DOWN:
4445 Feld[x][y] = EL_YAMYAM;
4446 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4449 case EL_SP_SNIKSNAK:
4450 MovDir[x][y] = MV_UP;
4453 case EL_SP_ELECTRON:
4454 MovDir[x][y] = MV_LEFT;
4461 Feld[x][y] = EL_MOLE;
4462 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4466 if (IS_CUSTOM_ELEMENT(element))
4468 struct ElementInfo *ei = &element_info[element];
4469 int move_direction_initial = ei->move_direction_initial;
4470 int move_pattern = ei->move_pattern;
4472 if (move_direction_initial == MV_START_PREVIOUS)
4474 if (MovDir[x][y] != MV_NONE)
4477 move_direction_initial = MV_START_AUTOMATIC;
4480 if (move_direction_initial == MV_START_RANDOM)
4481 MovDir[x][y] = 1 << RND(4);
4482 else if (move_direction_initial & MV_ANY_DIRECTION)
4483 MovDir[x][y] = move_direction_initial;
4484 else if (move_pattern == MV_ALL_DIRECTIONS ||
4485 move_pattern == MV_TURNING_LEFT ||
4486 move_pattern == MV_TURNING_RIGHT ||
4487 move_pattern == MV_TURNING_LEFT_RIGHT ||
4488 move_pattern == MV_TURNING_RIGHT_LEFT ||
4489 move_pattern == MV_TURNING_RANDOM)
4490 MovDir[x][y] = 1 << RND(4);
4491 else if (move_pattern == MV_HORIZONTAL)
4492 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4493 else if (move_pattern == MV_VERTICAL)
4494 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4495 else if (move_pattern & MV_ANY_DIRECTION)
4496 MovDir[x][y] = element_info[element].move_pattern;
4497 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4498 move_pattern == MV_ALONG_RIGHT_SIDE)
4500 /* use random direction as default start direction */
4501 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4502 MovDir[x][y] = 1 << RND(4);
4504 for (i = 0; i < NUM_DIRECTIONS; i++)
4506 int x1 = x + xy[i][0];
4507 int y1 = y + xy[i][1];
4509 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4511 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4512 MovDir[x][y] = direction[0][i];
4514 MovDir[x][y] = direction[1][i];
4523 MovDir[x][y] = 1 << RND(4);
4525 if (element != EL_BUG &&
4526 element != EL_SPACESHIP &&
4527 element != EL_BD_BUTTERFLY &&
4528 element != EL_BD_FIREFLY)
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 (element == EL_BUG || element == EL_BD_BUTTERFLY)
4540 MovDir[x][y] = direction[0][i];
4543 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4544 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4546 MovDir[x][y] = direction[1][i];
4555 GfxDir[x][y] = MovDir[x][y];
4558 void InitAmoebaNr(int x, int y)
4561 int group_nr = AmoebeNachbarNr(x, y);
4565 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4567 if (AmoebaCnt[i] == 0)
4575 AmoebaNr[x][y] = group_nr;
4576 AmoebaCnt[group_nr]++;
4577 AmoebaCnt2[group_nr]++;
4580 static void PlayerWins(struct PlayerInfo *player)
4582 player->LevelSolved = TRUE;
4583 player->GameOver = TRUE;
4585 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4586 level.native_em_level->lev->score : player->score);
4588 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4589 player->LevelSolved_CountingScore = player->score_final;
4594 static int time, time_final;
4595 static int score, score_final;
4596 static int game_over_delay_1 = 0;
4597 static int game_over_delay_2 = 0;
4598 int game_over_delay_value_1 = 50;
4599 int game_over_delay_value_2 = 50;
4601 if (!local_player->LevelSolved_GameWon)
4605 /* do not start end game actions before the player stops moving (to exit) */
4606 if (local_player->MovPos)
4609 local_player->LevelSolved_GameWon = TRUE;
4610 local_player->LevelSolved_SaveTape = tape.recording;
4611 local_player->LevelSolved_SaveScore = !tape.playing;
4613 if (tape.auto_play) /* tape might already be stopped here */
4614 tape.auto_play_level_solved = TRUE;
4620 game_over_delay_1 = game_over_delay_value_1;
4621 game_over_delay_2 = game_over_delay_value_2;
4623 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4624 score = score_final = local_player->score_final;
4629 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4631 else if (level.time == 0 && TimePlayed < 999)
4634 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4637 local_player->score_final = score_final;
4639 if (level_editor_test_game)
4642 score = score_final;
4645 local_player->LevelSolved_CountingTime = time;
4646 local_player->LevelSolved_CountingScore = score;
4648 game_panel_controls[GAME_PANEL_TIME].value = time;
4649 game_panel_controls[GAME_PANEL_SCORE].value = score;
4651 DisplayGameControlValues();
4653 DrawGameValue_Time(time);
4654 DrawGameValue_Score(score);
4658 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4660 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4662 /* close exit door after last player */
4663 if ((AllPlayersGone &&
4664 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4665 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4666 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4667 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4668 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4670 int element = Feld[ExitX][ExitY];
4673 if (element == EL_EM_EXIT_OPEN ||
4674 element == EL_EM_STEEL_EXIT_OPEN)
4681 Feld[ExitX][ExitY] =
4682 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4683 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4684 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4685 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4686 EL_EM_STEEL_EXIT_CLOSING);
4688 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4692 /* player disappears */
4693 DrawLevelField(ExitX, ExitY);
4696 for (i = 0; i < MAX_PLAYERS; i++)
4698 struct PlayerInfo *player = &stored_player[i];
4700 if (player->present)
4702 RemovePlayer(player);
4704 /* player disappears */
4705 DrawLevelField(player->jx, player->jy);
4710 PlaySound(SND_GAME_WINNING);
4713 if (game_over_delay_1 > 0)
4715 game_over_delay_1--;
4720 if (time != time_final)
4722 int time_to_go = ABS(time_final - time);
4723 int time_count_dir = (time < time_final ? +1 : -1);
4724 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4726 time += time_count_steps * time_count_dir;
4727 score += time_count_steps * level.score[SC_TIME_BONUS];
4730 local_player->LevelSolved_CountingTime = time;
4731 local_player->LevelSolved_CountingScore = score;
4733 game_panel_controls[GAME_PANEL_TIME].value = time;
4734 game_panel_controls[GAME_PANEL_SCORE].value = score;
4736 DisplayGameControlValues();
4738 DrawGameValue_Time(time);
4739 DrawGameValue_Score(score);
4742 if (time == time_final)
4743 StopSound(SND_GAME_LEVELTIME_BONUS);
4744 else if (setup.sound_loops)
4745 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4747 PlaySound(SND_GAME_LEVELTIME_BONUS);
4752 local_player->LevelSolved_PanelOff = TRUE;
4754 if (game_over_delay_2 > 0)
4756 game_over_delay_2--;
4769 boolean raise_level = FALSE;
4771 local_player->LevelSolved_GameEnd = TRUE;
4773 CloseDoor(DOOR_CLOSE_1);
4775 if (local_player->LevelSolved_SaveTape)
4782 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4784 SaveTape(tape.level_nr); /* ask to save tape */
4788 if (level_editor_test_game)
4790 game_status = GAME_MODE_MAIN;
4793 DrawAndFadeInMainMenu(REDRAW_FIELD);
4801 if (!local_player->LevelSolved_SaveScore)
4804 FadeOut(REDRAW_FIELD);
4807 game_status = GAME_MODE_MAIN;
4809 DrawAndFadeInMainMenu(REDRAW_FIELD);
4814 if (level_nr == leveldir_current->handicap_level)
4816 leveldir_current->handicap_level++;
4817 SaveLevelSetup_SeriesInfo();
4820 if (level_nr < leveldir_current->last_level)
4821 raise_level = TRUE; /* advance to next level */
4823 if ((hi_pos = NewHiScore()) >= 0)
4825 game_status = GAME_MODE_SCORES;
4827 DrawHallOfFame(hi_pos);
4838 FadeOut(REDRAW_FIELD);
4841 game_status = GAME_MODE_MAIN;
4849 DrawAndFadeInMainMenu(REDRAW_FIELD);
4858 LoadScore(level_nr);
4860 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4861 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4864 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4866 if (local_player->score_final > highscore[k].Score)
4868 /* player has made it to the hall of fame */
4870 if (k < MAX_SCORE_ENTRIES - 1)
4872 int m = MAX_SCORE_ENTRIES - 1;
4875 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4876 if (strEqual(setup.player_name, highscore[l].Name))
4878 if (m == k) /* player's new highscore overwrites his old one */
4882 for (l = m; l > k; l--)
4884 strcpy(highscore[l].Name, highscore[l - 1].Name);
4885 highscore[l].Score = highscore[l - 1].Score;
4892 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4893 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4894 highscore[k].Score = local_player->score_final;
4900 else if (!strncmp(setup.player_name, highscore[k].Name,
4901 MAX_PLAYER_NAME_LEN))
4902 break; /* player already there with a higher score */
4908 SaveScore(level_nr);
4913 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4915 int element = Feld[x][y];
4916 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4917 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4918 int horiz_move = (dx != 0);
4919 int sign = (horiz_move ? dx : dy);
4920 int step = sign * element_info[element].move_stepsize;
4922 /* special values for move stepsize for spring and things on conveyor belt */
4925 if (CAN_FALL(element) &&
4926 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4927 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4928 else if (element == EL_SPRING)
4929 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4935 inline static int getElementMoveStepsize(int x, int y)
4937 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4940 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4942 if (player->GfxAction != action || player->GfxDir != dir)
4945 printf("Player frame reset! (%d => %d, %d => %d)\n",
4946 player->GfxAction, action, player->GfxDir, dir);
4949 player->GfxAction = action;
4950 player->GfxDir = dir;
4952 player->StepFrame = 0;
4956 #if USE_GFX_RESET_GFX_ANIMATION
4957 static void ResetGfxFrame(int x, int y, boolean redraw)
4959 int element = Feld[x][y];
4960 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4961 int last_gfx_frame = GfxFrame[x][y];
4963 if (graphic_info[graphic].anim_global_sync)
4964 GfxFrame[x][y] = FrameCounter;
4965 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4966 GfxFrame[x][y] = CustomValue[x][y];
4967 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4968 GfxFrame[x][y] = element_info[element].collect_score;
4969 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4970 GfxFrame[x][y] = ChangeDelay[x][y];
4972 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4973 DrawLevelGraphicAnimation(x, y, graphic);
4977 static void ResetGfxAnimation(int x, int y)
4979 GfxAction[x][y] = ACTION_DEFAULT;
4980 GfxDir[x][y] = MovDir[x][y];
4983 #if USE_GFX_RESET_GFX_ANIMATION
4984 ResetGfxFrame(x, y, FALSE);
4988 static void ResetRandomAnimationValue(int x, int y)
4990 GfxRandom[x][y] = INIT_GFX_RANDOM();
4993 void InitMovingField(int x, int y, int direction)
4995 int element = Feld[x][y];
4996 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4997 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5000 boolean is_moving_before, is_moving_after;
5002 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5005 /* check if element was/is moving or being moved before/after mode change */
5008 is_moving_before = (WasJustMoving[x][y] != 0);
5010 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5011 is_moving_before = WasJustMoving[x][y];
5014 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5016 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5018 /* reset animation only for moving elements which change direction of moving
5019 or which just started or stopped moving
5020 (else CEs with property "can move" / "not moving" are reset each frame) */
5021 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5023 if (is_moving_before != is_moving_after ||
5024 direction != MovDir[x][y])
5025 ResetGfxAnimation(x, y);
5027 if ((is_moving_before || is_moving_after) && !continues_moving)
5028 ResetGfxAnimation(x, y);
5031 if (!continues_moving)
5032 ResetGfxAnimation(x, y);
5035 MovDir[x][y] = direction;
5036 GfxDir[x][y] = direction;
5038 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5039 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5040 direction == MV_DOWN && CAN_FALL(element) ?
5041 ACTION_FALLING : ACTION_MOVING);
5043 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5044 ACTION_FALLING : ACTION_MOVING);
5047 /* this is needed for CEs with property "can move" / "not moving" */
5049 if (is_moving_after)
5051 if (Feld[newx][newy] == EL_EMPTY)
5052 Feld[newx][newy] = EL_BLOCKED;
5054 MovDir[newx][newy] = MovDir[x][y];
5056 #if USE_NEW_CUSTOM_VALUE
5057 CustomValue[newx][newy] = CustomValue[x][y];
5060 GfxFrame[newx][newy] = GfxFrame[x][y];
5061 GfxRandom[newx][newy] = GfxRandom[x][y];
5062 GfxAction[newx][newy] = GfxAction[x][y];
5063 GfxDir[newx][newy] = GfxDir[x][y];
5067 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5069 int direction = MovDir[x][y];
5070 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5071 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5077 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5079 int oldx = x, oldy = y;
5080 int direction = MovDir[x][y];
5082 if (direction == MV_LEFT)
5084 else if (direction == MV_RIGHT)
5086 else if (direction == MV_UP)
5088 else if (direction == MV_DOWN)
5091 *comes_from_x = oldx;
5092 *comes_from_y = oldy;
5095 int MovingOrBlocked2Element(int x, int y)
5097 int element = Feld[x][y];
5099 if (element == EL_BLOCKED)
5103 Blocked2Moving(x, y, &oldx, &oldy);
5104 return Feld[oldx][oldy];
5110 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5112 /* like MovingOrBlocked2Element(), but if element is moving
5113 and (x,y) is the field the moving element is just leaving,
5114 return EL_BLOCKED instead of the element value */
5115 int element = Feld[x][y];
5117 if (IS_MOVING(x, y))
5119 if (element == EL_BLOCKED)
5123 Blocked2Moving(x, y, &oldx, &oldy);
5124 return Feld[oldx][oldy];
5133 static void RemoveField(int x, int y)
5135 Feld[x][y] = EL_EMPTY;
5141 #if USE_NEW_CUSTOM_VALUE
5142 CustomValue[x][y] = 0;
5146 ChangeDelay[x][y] = 0;
5147 ChangePage[x][y] = -1;
5148 Pushed[x][y] = FALSE;
5151 ExplodeField[x][y] = EX_TYPE_NONE;
5154 GfxElement[x][y] = EL_UNDEFINED;
5155 GfxAction[x][y] = ACTION_DEFAULT;
5156 GfxDir[x][y] = MV_NONE;
5158 /* !!! this would prevent the removed tile from being redrawn !!! */
5159 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5163 void RemoveMovingField(int x, int y)
5165 int oldx = x, oldy = y, newx = x, newy = y;
5166 int element = Feld[x][y];
5167 int next_element = EL_UNDEFINED;
5169 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5172 if (IS_MOVING(x, y))
5174 Moving2Blocked(x, y, &newx, &newy);
5176 if (Feld[newx][newy] != EL_BLOCKED)
5178 /* element is moving, but target field is not free (blocked), but
5179 already occupied by something different (example: acid pool);
5180 in this case, only remove the moving field, but not the target */
5182 RemoveField(oldx, oldy);
5184 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5186 TEST_DrawLevelField(oldx, oldy);
5191 else if (element == EL_BLOCKED)
5193 Blocked2Moving(x, y, &oldx, &oldy);
5194 if (!IS_MOVING(oldx, oldy))
5198 if (element == EL_BLOCKED &&
5199 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5200 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5201 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5202 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5203 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5204 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5205 next_element = get_next_element(Feld[oldx][oldy]);
5207 RemoveField(oldx, oldy);
5208 RemoveField(newx, newy);
5210 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5212 if (next_element != EL_UNDEFINED)
5213 Feld[oldx][oldy] = next_element;
5215 TEST_DrawLevelField(oldx, oldy);
5216 TEST_DrawLevelField(newx, newy);
5219 void DrawDynamite(int x, int y)
5221 int sx = SCREENX(x), sy = SCREENY(y);
5222 int graphic = el2img(Feld[x][y]);
5225 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5228 if (IS_WALKABLE_INSIDE(Back[x][y]))
5232 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5233 else if (Store[x][y])
5234 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5236 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5238 if (Back[x][y] || Store[x][y])
5239 DrawGraphicThruMask(sx, sy, graphic, frame);
5241 DrawGraphic(sx, sy, graphic, frame);
5244 void CheckDynamite(int x, int y)
5246 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5250 if (MovDelay[x][y] != 0)
5253 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5259 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5264 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5266 boolean num_checked_players = 0;
5269 for (i = 0; i < MAX_PLAYERS; i++)
5271 if (stored_player[i].active)
5273 int sx = stored_player[i].jx;
5274 int sy = stored_player[i].jy;
5276 if (num_checked_players == 0)
5283 *sx1 = MIN(*sx1, sx);
5284 *sy1 = MIN(*sy1, sy);
5285 *sx2 = MAX(*sx2, sx);
5286 *sy2 = MAX(*sy2, sy);
5289 num_checked_players++;
5294 static boolean checkIfAllPlayersFitToScreen_RND()
5296 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5298 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5300 return (sx2 - sx1 < SCR_FIELDX &&
5301 sy2 - sy1 < SCR_FIELDY);
5304 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5306 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5308 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5310 *sx = (sx1 + sx2) / 2;
5311 *sy = (sy1 + sy2) / 2;
5314 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5315 boolean center_screen, boolean quick_relocation)
5317 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5318 boolean no_delay = (tape.warp_forward);
5319 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5320 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5322 if (quick_relocation)
5324 int offset = game.scroll_delay_value;
5326 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5328 if (!level.shifted_relocation || center_screen)
5330 /* quick relocation (without scrolling), with centering of screen */
5332 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5333 x > SBX_Right + MIDPOSX ? SBX_Right :
5336 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5337 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5342 /* quick relocation (without scrolling), but do not center screen */
5344 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5345 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5348 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5349 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5352 int offset_x = x + (scroll_x - center_scroll_x);
5353 int offset_y = y + (scroll_y - center_scroll_y);
5355 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5356 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5357 offset_x - MIDPOSX);
5359 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5360 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5361 offset_y - MIDPOSY);
5366 /* quick relocation (without scrolling), inside visible screen area */
5368 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5369 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5370 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5372 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5373 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5374 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5376 /* don't scroll over playfield boundaries */
5377 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5378 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5380 /* don't scroll over playfield boundaries */
5381 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5382 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5385 RedrawPlayfield(TRUE, 0,0,0,0);
5390 int scroll_xx, scroll_yy;
5392 if (!level.shifted_relocation || center_screen)
5394 /* visible relocation (with scrolling), with centering of screen */
5396 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5397 x > SBX_Right + MIDPOSX ? SBX_Right :
5400 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5401 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5406 /* visible relocation (with scrolling), but do not center screen */
5408 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5409 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5412 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5413 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5416 int offset_x = x + (scroll_x - center_scroll_x);
5417 int offset_y = y + (scroll_y - center_scroll_y);
5419 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5420 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5421 offset_x - MIDPOSX);
5423 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5424 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5425 offset_y - MIDPOSY);
5430 /* visible relocation (with scrolling), with centering of screen */
5432 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5433 x > SBX_Right + MIDPOSX ? SBX_Right :
5436 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5437 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5443 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5446 int fx = FX, fy = FY;
5448 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5449 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5451 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5457 fx += dx * TILEX / 2;
5458 fy += dy * TILEY / 2;
5460 ScrollLevel(dx, dy);
5463 /* scroll in two steps of half tile size to make things smoother */
5464 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5466 Delay(wait_delay_value);
5468 /* scroll second step to align at full tile size */
5470 Delay(wait_delay_value);
5475 Delay(wait_delay_value);
5479 void RelocatePlayer(int jx, int jy, int el_player_raw)
5481 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5482 int player_nr = GET_PLAYER_NR(el_player);
5483 struct PlayerInfo *player = &stored_player[player_nr];
5484 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5485 boolean no_delay = (tape.warp_forward);
5486 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5487 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5488 int old_jx = player->jx;
5489 int old_jy = player->jy;
5490 int old_element = Feld[old_jx][old_jy];
5491 int element = Feld[jx][jy];
5492 boolean player_relocated = (old_jx != jx || old_jy != jy);
5494 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5495 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5496 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5497 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5498 int leave_side_horiz = move_dir_horiz;
5499 int leave_side_vert = move_dir_vert;
5500 int enter_side = enter_side_horiz | enter_side_vert;
5501 int leave_side = leave_side_horiz | leave_side_vert;
5503 if (player->GameOver) /* do not reanimate dead player */
5506 if (!player_relocated) /* no need to relocate the player */
5509 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5511 RemoveField(jx, jy); /* temporarily remove newly placed player */
5512 DrawLevelField(jx, jy);
5515 if (player->present)
5517 while (player->MovPos)
5519 ScrollPlayer(player, SCROLL_GO_ON);
5520 ScrollScreen(NULL, SCROLL_GO_ON);
5522 AdvanceFrameAndPlayerCounters(player->index_nr);
5527 Delay(wait_delay_value);
5530 DrawPlayer(player); /* needed here only to cleanup last field */
5531 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5533 player->is_moving = FALSE;
5536 if (IS_CUSTOM_ELEMENT(old_element))
5537 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5539 player->index_bit, leave_side);
5541 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5543 player->index_bit, leave_side);
5545 Feld[jx][jy] = el_player;
5546 InitPlayerField(jx, jy, el_player, TRUE);
5548 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5550 Feld[jx][jy] = element;
5551 InitField(jx, jy, FALSE);
5554 /* only visually relocate centered player */
5555 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5556 FALSE, level.instant_relocation);
5558 TestIfPlayerTouchesBadThing(jx, jy);
5559 TestIfPlayerTouchesCustomElement(jx, jy);
5561 if (IS_CUSTOM_ELEMENT(element))
5562 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5563 player->index_bit, enter_side);
5565 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5566 player->index_bit, enter_side);
5569 void Explode(int ex, int ey, int phase, int mode)
5575 /* !!! eliminate this variable !!! */
5576 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5578 if (game.explosions_delayed)
5580 ExplodeField[ex][ey] = mode;
5584 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5586 int center_element = Feld[ex][ey];
5587 int artwork_element, explosion_element; /* set these values later */
5590 /* --- This is only really needed (and now handled) in "Impact()". --- */
5591 /* do not explode moving elements that left the explode field in time */
5592 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5593 center_element == EL_EMPTY &&
5594 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5599 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5600 if (mode == EX_TYPE_NORMAL ||
5601 mode == EX_TYPE_CENTER ||
5602 mode == EX_TYPE_CROSS)
5603 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5606 /* remove things displayed in background while burning dynamite */
5607 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5610 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5612 /* put moving element to center field (and let it explode there) */
5613 center_element = MovingOrBlocked2Element(ex, ey);
5614 RemoveMovingField(ex, ey);
5615 Feld[ex][ey] = center_element;
5618 /* now "center_element" is finally determined -- set related values now */
5619 artwork_element = center_element; /* for custom player artwork */
5620 explosion_element = center_element; /* for custom player artwork */
5622 if (IS_PLAYER(ex, ey))
5624 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5626 artwork_element = stored_player[player_nr].artwork_element;
5628 if (level.use_explosion_element[player_nr])
5630 explosion_element = level.explosion_element[player_nr];
5631 artwork_element = explosion_element;
5636 if (mode == EX_TYPE_NORMAL ||
5637 mode == EX_TYPE_CENTER ||
5638 mode == EX_TYPE_CROSS)
5639 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5642 last_phase = element_info[explosion_element].explosion_delay + 1;
5644 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5646 int xx = x - ex + 1;
5647 int yy = y - ey + 1;
5650 if (!IN_LEV_FIELD(x, y) ||
5651 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5652 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5655 element = Feld[x][y];
5657 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5659 element = MovingOrBlocked2Element(x, y);
5661 if (!IS_EXPLOSION_PROOF(element))
5662 RemoveMovingField(x, y);
5665 /* indestructible elements can only explode in center (but not flames) */
5666 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5667 mode == EX_TYPE_BORDER)) ||
5668 element == EL_FLAMES)
5671 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5672 behaviour, for example when touching a yamyam that explodes to rocks
5673 with active deadly shield, a rock is created under the player !!! */
5674 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5676 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5677 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5678 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5680 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5683 if (IS_ACTIVE_BOMB(element))
5685 /* re-activate things under the bomb like gate or penguin */
5686 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5693 /* save walkable background elements while explosion on same tile */
5694 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5695 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5696 Back[x][y] = element;
5698 /* ignite explodable elements reached by other explosion */
5699 if (element == EL_EXPLOSION)
5700 element = Store2[x][y];
5702 if (AmoebaNr[x][y] &&
5703 (element == EL_AMOEBA_FULL ||
5704 element == EL_BD_AMOEBA ||
5705 element == EL_AMOEBA_GROWING))
5707 AmoebaCnt[AmoebaNr[x][y]]--;
5708 AmoebaCnt2[AmoebaNr[x][y]]--;
5713 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5715 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5717 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5719 if (PLAYERINFO(ex, ey)->use_murphy)
5720 Store[x][y] = EL_EMPTY;
5723 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5724 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5725 else if (ELEM_IS_PLAYER(center_element))
5726 Store[x][y] = EL_EMPTY;
5727 else if (center_element == EL_YAMYAM)
5728 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5729 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5730 Store[x][y] = element_info[center_element].content.e[xx][yy];
5732 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5733 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5734 otherwise) -- FIX THIS !!! */
5735 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5736 Store[x][y] = element_info[element].content.e[1][1];
5738 else if (!CAN_EXPLODE(element))
5739 Store[x][y] = element_info[element].content.e[1][1];
5742 Store[x][y] = EL_EMPTY;
5744 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5745 center_element == EL_AMOEBA_TO_DIAMOND)
5746 Store2[x][y] = element;
5748 Feld[x][y] = EL_EXPLOSION;
5749 GfxElement[x][y] = artwork_element;
5751 ExplodePhase[x][y] = 1;
5752 ExplodeDelay[x][y] = last_phase;
5757 if (center_element == EL_YAMYAM)
5758 game.yamyam_content_nr =
5759 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5771 GfxFrame[x][y] = 0; /* restart explosion animation */
5773 last_phase = ExplodeDelay[x][y];
5775 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5779 /* activate this even in non-DEBUG version until cause for crash in
5780 getGraphicAnimationFrame() (see below) is found and eliminated */
5786 /* this can happen if the player leaves an explosion just in time */
5787 if (GfxElement[x][y] == EL_UNDEFINED)
5788 GfxElement[x][y] = EL_EMPTY;
5790 if (GfxElement[x][y] == EL_UNDEFINED)
5793 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5794 printf("Explode(): This should never happen!\n");
5797 GfxElement[x][y] = EL_EMPTY;
5803 border_element = Store2[x][y];
5804 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5805 border_element = StorePlayer[x][y];
5807 if (phase == element_info[border_element].ignition_delay ||
5808 phase == last_phase)
5810 boolean border_explosion = FALSE;
5812 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5813 !PLAYER_EXPLOSION_PROTECTED(x, y))
5815 KillPlayerUnlessExplosionProtected(x, y);
5816 border_explosion = TRUE;
5818 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5820 Feld[x][y] = Store2[x][y];
5823 border_explosion = TRUE;
5825 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5827 AmoebeUmwandeln(x, y);
5829 border_explosion = TRUE;
5832 /* if an element just explodes due to another explosion (chain-reaction),
5833 do not immediately end the new explosion when it was the last frame of
5834 the explosion (as it would be done in the following "if"-statement!) */
5835 if (border_explosion && phase == last_phase)
5839 if (phase == last_phase)
5843 element = Feld[x][y] = Store[x][y];
5844 Store[x][y] = Store2[x][y] = 0;
5845 GfxElement[x][y] = EL_UNDEFINED;
5847 /* player can escape from explosions and might therefore be still alive */
5848 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5849 element <= EL_PLAYER_IS_EXPLODING_4)
5851 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5852 int explosion_element = EL_PLAYER_1 + player_nr;
5853 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5854 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5856 if (level.use_explosion_element[player_nr])
5857 explosion_element = level.explosion_element[player_nr];
5859 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5860 element_info[explosion_element].content.e[xx][yy]);
5863 /* restore probably existing indestructible background element */
5864 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5865 element = Feld[x][y] = Back[x][y];
5868 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5869 GfxDir[x][y] = MV_NONE;
5870 ChangeDelay[x][y] = 0;
5871 ChangePage[x][y] = -1;
5873 #if USE_NEW_CUSTOM_VALUE
5874 CustomValue[x][y] = 0;
5877 InitField_WithBug2(x, y, FALSE);
5879 TEST_DrawLevelField(x, y);
5881 TestIfElementTouchesCustomElement(x, y);
5883 if (GFX_CRUMBLED(element))
5884 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5886 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5887 StorePlayer[x][y] = 0;
5889 if (ELEM_IS_PLAYER(element))
5890 RelocatePlayer(x, y, element);
5892 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5894 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5895 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5898 TEST_DrawLevelFieldCrumbledSand(x, y);
5900 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5902 DrawLevelElement(x, y, Back[x][y]);
5903 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5905 else if (IS_WALKABLE_UNDER(Back[x][y]))
5907 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5908 DrawLevelElementThruMask(x, y, Back[x][y]);
5910 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5911 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5915 void DynaExplode(int ex, int ey)
5918 int dynabomb_element = Feld[ex][ey];
5919 int dynabomb_size = 1;
5920 boolean dynabomb_xl = FALSE;
5921 struct PlayerInfo *player;
5922 static int xy[4][2] =
5930 if (IS_ACTIVE_BOMB(dynabomb_element))
5932 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5933 dynabomb_size = player->dynabomb_size;
5934 dynabomb_xl = player->dynabomb_xl;
5935 player->dynabombs_left++;
5938 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5940 for (i = 0; i < NUM_DIRECTIONS; i++)
5942 for (j = 1; j <= dynabomb_size; j++)
5944 int x = ex + j * xy[i][0];
5945 int y = ey + j * xy[i][1];
5948 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5951 element = Feld[x][y];
5953 /* do not restart explosions of fields with active bombs */
5954 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5957 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5959 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5960 !IS_DIGGABLE(element) && !dynabomb_xl)
5966 void Bang(int x, int y)
5968 int element = MovingOrBlocked2Element(x, y);
5969 int explosion_type = EX_TYPE_NORMAL;
5971 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5973 struct PlayerInfo *player = PLAYERINFO(x, y);
5975 #if USE_FIX_CE_ACTION_WITH_PLAYER
5976 element = Feld[x][y] = player->initial_element;
5978 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5979 player->element_nr);
5982 if (level.use_explosion_element[player->index_nr])
5984 int explosion_element = level.explosion_element[player->index_nr];
5986 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5987 explosion_type = EX_TYPE_CROSS;
5988 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5989 explosion_type = EX_TYPE_CENTER;
5997 case EL_BD_BUTTERFLY:
6000 case EL_DARK_YAMYAM:
6004 RaiseScoreElement(element);
6007 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6008 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6009 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6010 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6011 case EL_DYNABOMB_INCREASE_NUMBER:
6012 case EL_DYNABOMB_INCREASE_SIZE:
6013 case EL_DYNABOMB_INCREASE_POWER:
6014 explosion_type = EX_TYPE_DYNA;
6017 case EL_DC_LANDMINE:
6019 case EL_EM_EXIT_OPEN:
6020 case EL_EM_STEEL_EXIT_OPEN:
6022 explosion_type = EX_TYPE_CENTER;
6027 case EL_LAMP_ACTIVE:
6028 case EL_AMOEBA_TO_DIAMOND:
6029 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6030 explosion_type = EX_TYPE_CENTER;
6034 if (element_info[element].explosion_type == EXPLODES_CROSS)
6035 explosion_type = EX_TYPE_CROSS;
6036 else if (element_info[element].explosion_type == EXPLODES_1X1)
6037 explosion_type = EX_TYPE_CENTER;
6041 if (explosion_type == EX_TYPE_DYNA)
6044 Explode(x, y, EX_PHASE_START, explosion_type);
6046 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6049 void SplashAcid(int x, int y)
6051 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6052 (!IN_LEV_FIELD(x - 1, y - 2) ||
6053 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6054 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6056 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6057 (!IN_LEV_FIELD(x + 1, y - 2) ||
6058 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6059 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6061 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6064 static void InitBeltMovement()
6066 static int belt_base_element[4] =
6068 EL_CONVEYOR_BELT_1_LEFT,
6069 EL_CONVEYOR_BELT_2_LEFT,
6070 EL_CONVEYOR_BELT_3_LEFT,
6071 EL_CONVEYOR_BELT_4_LEFT
6073 static int belt_base_active_element[4] =
6075 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6076 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6077 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6078 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6083 /* set frame order for belt animation graphic according to belt direction */
6084 for (i = 0; i < NUM_BELTS; i++)
6088 for (j = 0; j < NUM_BELT_PARTS; j++)
6090 int element = belt_base_active_element[belt_nr] + j;
6091 int graphic_1 = el2img(element);
6092 int graphic_2 = el2panelimg(element);
6094 if (game.belt_dir[i] == MV_LEFT)
6096 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6097 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6101 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6102 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6107 SCAN_PLAYFIELD(x, y)
6109 int element = Feld[x][y];
6111 for (i = 0; i < NUM_BELTS; i++)
6113 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6115 int e_belt_nr = getBeltNrFromBeltElement(element);
6118 if (e_belt_nr == belt_nr)
6120 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6122 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6129 static void ToggleBeltSwitch(int x, int y)
6131 static int belt_base_element[4] =
6133 EL_CONVEYOR_BELT_1_LEFT,
6134 EL_CONVEYOR_BELT_2_LEFT,
6135 EL_CONVEYOR_BELT_3_LEFT,
6136 EL_CONVEYOR_BELT_4_LEFT
6138 static int belt_base_active_element[4] =
6140 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6141 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6142 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6143 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6145 static int belt_base_switch_element[4] =
6147 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6148 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6149 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6150 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6152 static int belt_move_dir[4] =
6160 int element = Feld[x][y];
6161 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6162 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6163 int belt_dir = belt_move_dir[belt_dir_nr];
6166 if (!IS_BELT_SWITCH(element))
6169 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6170 game.belt_dir[belt_nr] = belt_dir;
6172 if (belt_dir_nr == 3)
6175 /* set frame order for belt animation graphic according to belt direction */
6176 for (i = 0; i < NUM_BELT_PARTS; i++)
6178 int element = belt_base_active_element[belt_nr] + i;
6179 int graphic_1 = el2img(element);
6180 int graphic_2 = el2panelimg(element);
6182 if (belt_dir == MV_LEFT)
6184 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6185 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6189 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6190 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6194 SCAN_PLAYFIELD(xx, yy)
6196 int element = Feld[xx][yy];
6198 if (IS_BELT_SWITCH(element))
6200 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6202 if (e_belt_nr == belt_nr)
6204 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6205 TEST_DrawLevelField(xx, yy);
6208 else if (IS_BELT(element) && belt_dir != MV_NONE)
6210 int e_belt_nr = getBeltNrFromBeltElement(element);
6212 if (e_belt_nr == belt_nr)
6214 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6216 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6217 TEST_DrawLevelField(xx, yy);
6220 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6222 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6224 if (e_belt_nr == belt_nr)
6226 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6228 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6229 TEST_DrawLevelField(xx, yy);
6235 static void ToggleSwitchgateSwitch(int x, int y)
6239 game.switchgate_pos = !game.switchgate_pos;
6241 SCAN_PLAYFIELD(xx, yy)
6243 int element = Feld[xx][yy];
6245 #if !USE_BOTH_SWITCHGATE_SWITCHES
6246 if (element == EL_SWITCHGATE_SWITCH_UP ||
6247 element == EL_SWITCHGATE_SWITCH_DOWN)
6249 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6250 TEST_DrawLevelField(xx, yy);
6252 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6253 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6255 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6256 TEST_DrawLevelField(xx, yy);
6259 if (element == EL_SWITCHGATE_SWITCH_UP)
6261 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6262 TEST_DrawLevelField(xx, yy);
6264 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6266 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6267 TEST_DrawLevelField(xx, yy);
6269 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6271 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6272 TEST_DrawLevelField(xx, yy);
6274 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6276 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6277 TEST_DrawLevelField(xx, yy);
6280 else if (element == EL_SWITCHGATE_OPEN ||
6281 element == EL_SWITCHGATE_OPENING)
6283 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6285 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6287 else if (element == EL_SWITCHGATE_CLOSED ||
6288 element == EL_SWITCHGATE_CLOSING)
6290 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6292 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6297 static int getInvisibleActiveFromInvisibleElement(int element)
6299 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6300 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6301 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6305 static int getInvisibleFromInvisibleActiveElement(int element)
6307 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6308 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6309 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6313 static void RedrawAllLightSwitchesAndInvisibleElements()
6317 SCAN_PLAYFIELD(x, y)
6319 int element = Feld[x][y];
6321 if (element == EL_LIGHT_SWITCH &&
6322 game.light_time_left > 0)
6324 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6325 TEST_DrawLevelField(x, y);
6327 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6328 game.light_time_left == 0)
6330 Feld[x][y] = EL_LIGHT_SWITCH;
6331 TEST_DrawLevelField(x, y);
6333 else if (element == EL_EMC_DRIPPER &&
6334 game.light_time_left > 0)
6336 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6337 TEST_DrawLevelField(x, y);
6339 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6340 game.light_time_left == 0)
6342 Feld[x][y] = EL_EMC_DRIPPER;
6343 TEST_DrawLevelField(x, y);
6345 else if (element == EL_INVISIBLE_STEELWALL ||
6346 element == EL_INVISIBLE_WALL ||
6347 element == EL_INVISIBLE_SAND)
6349 if (game.light_time_left > 0)
6350 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6352 TEST_DrawLevelField(x, y);
6354 /* uncrumble neighbour fields, if needed */
6355 if (element == EL_INVISIBLE_SAND)
6356 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6358 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6359 element == EL_INVISIBLE_WALL_ACTIVE ||
6360 element == EL_INVISIBLE_SAND_ACTIVE)
6362 if (game.light_time_left == 0)
6363 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6365 TEST_DrawLevelField(x, y);
6367 /* re-crumble neighbour fields, if needed */
6368 if (element == EL_INVISIBLE_SAND)
6369 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6374 static void RedrawAllInvisibleElementsForLenses()
6378 SCAN_PLAYFIELD(x, y)
6380 int element = Feld[x][y];
6382 if (element == EL_EMC_DRIPPER &&
6383 game.lenses_time_left > 0)
6385 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6386 TEST_DrawLevelField(x, y);
6388 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6389 game.lenses_time_left == 0)
6391 Feld[x][y] = EL_EMC_DRIPPER;
6392 TEST_DrawLevelField(x, y);
6394 else if (element == EL_INVISIBLE_STEELWALL ||
6395 element == EL_INVISIBLE_WALL ||
6396 element == EL_INVISIBLE_SAND)
6398 if (game.lenses_time_left > 0)
6399 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6401 TEST_DrawLevelField(x, y);
6403 /* uncrumble neighbour fields, if needed */
6404 if (element == EL_INVISIBLE_SAND)
6405 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6407 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6408 element == EL_INVISIBLE_WALL_ACTIVE ||
6409 element == EL_INVISIBLE_SAND_ACTIVE)
6411 if (game.lenses_time_left == 0)
6412 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6414 TEST_DrawLevelField(x, y);
6416 /* re-crumble neighbour fields, if needed */
6417 if (element == EL_INVISIBLE_SAND)
6418 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6423 static void RedrawAllInvisibleElementsForMagnifier()
6427 SCAN_PLAYFIELD(x, y)
6429 int element = Feld[x][y];
6431 if (element == EL_EMC_FAKE_GRASS &&
6432 game.magnify_time_left > 0)
6434 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6435 TEST_DrawLevelField(x, y);
6437 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6438 game.magnify_time_left == 0)
6440 Feld[x][y] = EL_EMC_FAKE_GRASS;
6441 TEST_DrawLevelField(x, y);
6443 else if (IS_GATE_GRAY(element) &&
6444 game.magnify_time_left > 0)
6446 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6447 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6448 IS_EM_GATE_GRAY(element) ?
6449 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6450 IS_EMC_GATE_GRAY(element) ?
6451 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6452 IS_DC_GATE_GRAY(element) ?
6453 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6455 TEST_DrawLevelField(x, y);
6457 else if (IS_GATE_GRAY_ACTIVE(element) &&
6458 game.magnify_time_left == 0)
6460 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6461 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6462 IS_EM_GATE_GRAY_ACTIVE(element) ?
6463 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6464 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6465 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6466 IS_DC_GATE_GRAY_ACTIVE(element) ?
6467 EL_DC_GATE_WHITE_GRAY :
6469 TEST_DrawLevelField(x, y);
6474 static void ToggleLightSwitch(int x, int y)
6476 int element = Feld[x][y];
6478 game.light_time_left =
6479 (element == EL_LIGHT_SWITCH ?
6480 level.time_light * FRAMES_PER_SECOND : 0);
6482 RedrawAllLightSwitchesAndInvisibleElements();
6485 static void ActivateTimegateSwitch(int x, int y)
6489 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6491 SCAN_PLAYFIELD(xx, yy)
6493 int element = Feld[xx][yy];
6495 if (element == EL_TIMEGATE_CLOSED ||
6496 element == EL_TIMEGATE_CLOSING)
6498 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6499 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6503 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6505 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6506 TEST_DrawLevelField(xx, yy);
6513 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6514 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6516 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6520 void Impact(int x, int y)
6522 boolean last_line = (y == lev_fieldy - 1);
6523 boolean object_hit = FALSE;
6524 boolean impact = (last_line || object_hit);
6525 int element = Feld[x][y];
6526 int smashed = EL_STEELWALL;
6528 if (!last_line) /* check if element below was hit */
6530 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6533 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6534 MovDir[x][y + 1] != MV_DOWN ||
6535 MovPos[x][y + 1] <= TILEY / 2));
6537 /* do not smash moving elements that left the smashed field in time */
6538 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6539 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6542 #if USE_QUICKSAND_IMPACT_BUGFIX
6543 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6545 RemoveMovingField(x, y + 1);
6546 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6547 Feld[x][y + 2] = EL_ROCK;
6548 TEST_DrawLevelField(x, y + 2);
6553 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6555 RemoveMovingField(x, y + 1);
6556 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6557 Feld[x][y + 2] = EL_ROCK;
6558 TEST_DrawLevelField(x, y + 2);
6565 smashed = MovingOrBlocked2Element(x, y + 1);
6567 impact = (last_line || object_hit);
6570 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6572 SplashAcid(x, y + 1);
6576 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6577 /* only reset graphic animation if graphic really changes after impact */
6579 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6581 ResetGfxAnimation(x, y);
6582 TEST_DrawLevelField(x, y);
6585 if (impact && CAN_EXPLODE_IMPACT(element))
6590 else if (impact && element == EL_PEARL &&
6591 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6593 ResetGfxAnimation(x, y);
6595 Feld[x][y] = EL_PEARL_BREAKING;
6596 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6599 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6601 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6606 if (impact && element == EL_AMOEBA_DROP)
6608 if (object_hit && IS_PLAYER(x, y + 1))
6609 KillPlayerUnlessEnemyProtected(x, y + 1);
6610 else if (object_hit && smashed == EL_PENGUIN)
6614 Feld[x][y] = EL_AMOEBA_GROWING;
6615 Store[x][y] = EL_AMOEBA_WET;
6617 ResetRandomAnimationValue(x, y);
6622 if (object_hit) /* check which object was hit */
6624 if ((CAN_PASS_MAGIC_WALL(element) &&
6625 (smashed == EL_MAGIC_WALL ||
6626 smashed == EL_BD_MAGIC_WALL)) ||
6627 (CAN_PASS_DC_MAGIC_WALL(element) &&
6628 smashed == EL_DC_MAGIC_WALL))
6631 int activated_magic_wall =
6632 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6633 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6634 EL_DC_MAGIC_WALL_ACTIVE);
6636 /* activate magic wall / mill */
6637 SCAN_PLAYFIELD(xx, yy)
6639 if (Feld[xx][yy] == smashed)
6640 Feld[xx][yy] = activated_magic_wall;
6643 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6644 game.magic_wall_active = TRUE;
6646 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6647 SND_MAGIC_WALL_ACTIVATING :
6648 smashed == EL_BD_MAGIC_WALL ?
6649 SND_BD_MAGIC_WALL_ACTIVATING :
6650 SND_DC_MAGIC_WALL_ACTIVATING));
6653 if (IS_PLAYER(x, y + 1))
6655 if (CAN_SMASH_PLAYER(element))
6657 KillPlayerUnlessEnemyProtected(x, y + 1);
6661 else if (smashed == EL_PENGUIN)
6663 if (CAN_SMASH_PLAYER(element))
6669 else if (element == EL_BD_DIAMOND)
6671 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6677 else if (((element == EL_SP_INFOTRON ||
6678 element == EL_SP_ZONK) &&
6679 (smashed == EL_SP_SNIKSNAK ||
6680 smashed == EL_SP_ELECTRON ||
6681 smashed == EL_SP_DISK_ORANGE)) ||
6682 (element == EL_SP_INFOTRON &&
6683 smashed == EL_SP_DISK_YELLOW))
6688 else if (CAN_SMASH_EVERYTHING(element))
6690 if (IS_CLASSIC_ENEMY(smashed) ||
6691 CAN_EXPLODE_SMASHED(smashed))
6696 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6698 if (smashed == EL_LAMP ||
6699 smashed == EL_LAMP_ACTIVE)
6704 else if (smashed == EL_NUT)
6706 Feld[x][y + 1] = EL_NUT_BREAKING;
6707 PlayLevelSound(x, y, SND_NUT_BREAKING);
6708 RaiseScoreElement(EL_NUT);
6711 else if (smashed == EL_PEARL)
6713 ResetGfxAnimation(x, y);
6715 Feld[x][y + 1] = EL_PEARL_BREAKING;
6716 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6719 else if (smashed == EL_DIAMOND)
6721 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6722 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6725 else if (IS_BELT_SWITCH(smashed))
6727 ToggleBeltSwitch(x, y + 1);
6729 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6730 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6731 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6732 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6734 ToggleSwitchgateSwitch(x, y + 1);
6736 else if (smashed == EL_LIGHT_SWITCH ||
6737 smashed == EL_LIGHT_SWITCH_ACTIVE)
6739 ToggleLightSwitch(x, y + 1);
6744 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6747 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6749 CheckElementChangeBySide(x, y + 1, smashed, element,
6750 CE_SWITCHED, CH_SIDE_TOP);
6751 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6757 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6762 /* play sound of magic wall / mill */
6764 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6765 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6766 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6768 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6769 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6770 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6771 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6772 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6773 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6778 /* play sound of object that hits the ground */
6779 if (last_line || object_hit)
6780 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6783 inline static void TurnRoundExt(int x, int y)
6795 { 0, 0 }, { 0, 0 }, { 0, 0 },
6800 int left, right, back;
6804 { MV_DOWN, MV_UP, MV_RIGHT },
6805 { MV_UP, MV_DOWN, MV_LEFT },
6807 { MV_LEFT, MV_RIGHT, MV_DOWN },
6811 { MV_RIGHT, MV_LEFT, MV_UP }
6814 int element = Feld[x][y];
6815 int move_pattern = element_info[element].move_pattern;
6817 int old_move_dir = MovDir[x][y];
6818 int left_dir = turn[old_move_dir].left;
6819 int right_dir = turn[old_move_dir].right;
6820 int back_dir = turn[old_move_dir].back;
6822 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6823 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6824 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6825 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6827 int left_x = x + left_dx, left_y = y + left_dy;
6828 int right_x = x + right_dx, right_y = y + right_dy;
6829 int move_x = x + move_dx, move_y = y + move_dy;
6833 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6835 TestIfBadThingTouchesOtherBadThing(x, y);
6837 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6838 MovDir[x][y] = right_dir;
6839 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6840 MovDir[x][y] = left_dir;
6842 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6844 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6847 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6849 TestIfBadThingTouchesOtherBadThing(x, y);
6851 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6852 MovDir[x][y] = left_dir;
6853 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6854 MovDir[x][y] = right_dir;
6856 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6858 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6861 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6863 TestIfBadThingTouchesOtherBadThing(x, y);
6865 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6866 MovDir[x][y] = left_dir;
6867 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6868 MovDir[x][y] = right_dir;
6870 if (MovDir[x][y] != old_move_dir)
6873 else if (element == EL_YAMYAM)
6875 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6876 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6878 if (can_turn_left && can_turn_right)
6879 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6880 else if (can_turn_left)
6881 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6882 else if (can_turn_right)
6883 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6885 MovDir[x][y] = back_dir;
6887 MovDelay[x][y] = 16 + 16 * RND(3);
6889 else if (element == EL_DARK_YAMYAM)
6891 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6893 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6896 if (can_turn_left && can_turn_right)
6897 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6898 else if (can_turn_left)
6899 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6900 else if (can_turn_right)
6901 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6903 MovDir[x][y] = back_dir;
6905 MovDelay[x][y] = 16 + 16 * RND(3);
6907 else if (element == EL_PACMAN)
6909 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6910 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6912 if (can_turn_left && can_turn_right)
6913 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6914 else if (can_turn_left)
6915 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6916 else if (can_turn_right)
6917 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6919 MovDir[x][y] = back_dir;
6921 MovDelay[x][y] = 6 + RND(40);
6923 else if (element == EL_PIG)
6925 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6926 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6927 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6928 boolean should_turn_left, should_turn_right, should_move_on;
6930 int rnd = RND(rnd_value);
6932 should_turn_left = (can_turn_left &&
6934 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6935 y + back_dy + left_dy)));
6936 should_turn_right = (can_turn_right &&
6938 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6939 y + back_dy + right_dy)));
6940 should_move_on = (can_move_on &&
6943 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6944 y + move_dy + left_dy) ||
6945 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6946 y + move_dy + right_dy)));
6948 if (should_turn_left || should_turn_right || should_move_on)
6950 if (should_turn_left && should_turn_right && should_move_on)
6951 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6952 rnd < 2 * rnd_value / 3 ? right_dir :
6954 else if (should_turn_left && should_turn_right)
6955 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6956 else if (should_turn_left && should_move_on)
6957 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6958 else if (should_turn_right && should_move_on)
6959 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6960 else if (should_turn_left)
6961 MovDir[x][y] = left_dir;
6962 else if (should_turn_right)
6963 MovDir[x][y] = right_dir;
6964 else if (should_move_on)
6965 MovDir[x][y] = old_move_dir;
6967 else if (can_move_on && rnd > rnd_value / 8)
6968 MovDir[x][y] = old_move_dir;
6969 else if (can_turn_left && can_turn_right)
6970 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6971 else if (can_turn_left && rnd > rnd_value / 8)
6972 MovDir[x][y] = left_dir;
6973 else if (can_turn_right && rnd > rnd_value/8)
6974 MovDir[x][y] = right_dir;
6976 MovDir[x][y] = back_dir;
6978 xx = x + move_xy[MovDir[x][y]].dx;
6979 yy = y + move_xy[MovDir[x][y]].dy;
6981 if (!IN_LEV_FIELD(xx, yy) ||
6982 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6983 MovDir[x][y] = old_move_dir;
6987 else if (element == EL_DRAGON)
6989 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6990 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6991 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6993 int rnd = RND(rnd_value);
6995 if (can_move_on && rnd > rnd_value / 8)
6996 MovDir[x][y] = old_move_dir;
6997 else if (can_turn_left && can_turn_right)
6998 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6999 else if (can_turn_left && rnd > rnd_value / 8)
7000 MovDir[x][y] = left_dir;
7001 else if (can_turn_right && rnd > rnd_value / 8)
7002 MovDir[x][y] = right_dir;
7004 MovDir[x][y] = back_dir;
7006 xx = x + move_xy[MovDir[x][y]].dx;
7007 yy = y + move_xy[MovDir[x][y]].dy;
7009 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7010 MovDir[x][y] = old_move_dir;
7014 else if (element == EL_MOLE)
7016 boolean can_move_on =
7017 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7018 IS_AMOEBOID(Feld[move_x][move_y]) ||
7019 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7022 boolean can_turn_left =
7023 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7024 IS_AMOEBOID(Feld[left_x][left_y])));
7026 boolean can_turn_right =
7027 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7028 IS_AMOEBOID(Feld[right_x][right_y])));
7030 if (can_turn_left && can_turn_right)
7031 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7032 else if (can_turn_left)
7033 MovDir[x][y] = left_dir;
7035 MovDir[x][y] = right_dir;
7038 if (MovDir[x][y] != old_move_dir)
7041 else if (element == EL_BALLOON)
7043 MovDir[x][y] = game.wind_direction;
7046 else if (element == EL_SPRING)
7048 #if USE_NEW_SPRING_BUMPER
7049 if (MovDir[x][y] & MV_HORIZONTAL)
7051 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7052 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7054 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7055 ResetGfxAnimation(move_x, move_y);
7056 TEST_DrawLevelField(move_x, move_y);
7058 MovDir[x][y] = back_dir;
7060 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7061 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7062 MovDir[x][y] = MV_NONE;
7065 if (MovDir[x][y] & MV_HORIZONTAL &&
7066 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7067 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7068 MovDir[x][y] = MV_NONE;
7073 else if (element == EL_ROBOT ||
7074 element == EL_SATELLITE ||
7075 element == EL_PENGUIN ||
7076 element == EL_EMC_ANDROID)
7078 int attr_x = -1, attr_y = -1;
7089 for (i = 0; i < MAX_PLAYERS; i++)
7091 struct PlayerInfo *player = &stored_player[i];
7092 int jx = player->jx, jy = player->jy;
7094 if (!player->active)
7098 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7106 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7107 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7108 game.engine_version < VERSION_IDENT(3,1,0,0)))
7114 if (element == EL_PENGUIN)
7117 static int xy[4][2] =
7125 for (i = 0; i < NUM_DIRECTIONS; i++)
7127 int ex = x + xy[i][0];
7128 int ey = y + xy[i][1];
7130 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7131 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7132 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7133 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7142 MovDir[x][y] = MV_NONE;
7144 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7145 else if (attr_x > x)
7146 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7148 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7149 else if (attr_y > y)
7150 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7152 if (element == EL_ROBOT)
7156 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7157 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7158 Moving2Blocked(x, y, &newx, &newy);
7160 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7161 MovDelay[x][y] = 8 + 8 * !RND(3);
7163 MovDelay[x][y] = 16;
7165 else if (element == EL_PENGUIN)
7171 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7173 boolean first_horiz = RND(2);
7174 int new_move_dir = MovDir[x][y];
7177 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7178 Moving2Blocked(x, y, &newx, &newy);
7180 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7184 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7185 Moving2Blocked(x, y, &newx, &newy);
7187 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7190 MovDir[x][y] = old_move_dir;
7194 else if (element == EL_SATELLITE)
7200 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7202 boolean first_horiz = RND(2);
7203 int new_move_dir = MovDir[x][y];
7206 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7207 Moving2Blocked(x, y, &newx, &newy);
7209 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7213 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7214 Moving2Blocked(x, y, &newx, &newy);
7216 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7219 MovDir[x][y] = old_move_dir;
7223 else if (element == EL_EMC_ANDROID)
7225 static int check_pos[16] =
7227 -1, /* 0 => (invalid) */
7228 7, /* 1 => MV_LEFT */
7229 3, /* 2 => MV_RIGHT */
7230 -1, /* 3 => (invalid) */
7232 0, /* 5 => MV_LEFT | MV_UP */
7233 2, /* 6 => MV_RIGHT | MV_UP */
7234 -1, /* 7 => (invalid) */
7235 5, /* 8 => MV_DOWN */
7236 6, /* 9 => MV_LEFT | MV_DOWN */
7237 4, /* 10 => MV_RIGHT | MV_DOWN */
7238 -1, /* 11 => (invalid) */
7239 -1, /* 12 => (invalid) */
7240 -1, /* 13 => (invalid) */
7241 -1, /* 14 => (invalid) */
7242 -1, /* 15 => (invalid) */
7250 { -1, -1, MV_LEFT | MV_UP },
7252 { +1, -1, MV_RIGHT | MV_UP },
7253 { +1, 0, MV_RIGHT },
7254 { +1, +1, MV_RIGHT | MV_DOWN },
7256 { -1, +1, MV_LEFT | MV_DOWN },
7259 int start_pos, check_order;
7260 boolean can_clone = FALSE;
7263 /* check if there is any free field around current position */
7264 for (i = 0; i < 8; i++)
7266 int newx = x + check_xy[i].dx;
7267 int newy = y + check_xy[i].dy;
7269 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7277 if (can_clone) /* randomly find an element to clone */
7281 start_pos = check_pos[RND(8)];
7282 check_order = (RND(2) ? -1 : +1);
7284 for (i = 0; i < 8; i++)
7286 int pos_raw = start_pos + i * check_order;
7287 int pos = (pos_raw + 8) % 8;
7288 int newx = x + check_xy[pos].dx;
7289 int newy = y + check_xy[pos].dy;
7291 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7293 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7294 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7296 Store[x][y] = Feld[newx][newy];
7305 if (can_clone) /* randomly find a direction to move */
7309 start_pos = check_pos[RND(8)];
7310 check_order = (RND(2) ? -1 : +1);
7312 for (i = 0; i < 8; i++)
7314 int pos_raw = start_pos + i * check_order;
7315 int pos = (pos_raw + 8) % 8;
7316 int newx = x + check_xy[pos].dx;
7317 int newy = y + check_xy[pos].dy;
7318 int new_move_dir = check_xy[pos].dir;
7320 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7322 MovDir[x][y] = new_move_dir;
7323 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7332 if (can_clone) /* cloning and moving successful */
7335 /* cannot clone -- try to move towards player */
7337 start_pos = check_pos[MovDir[x][y] & 0x0f];
7338 check_order = (RND(2) ? -1 : +1);
7340 for (i = 0; i < 3; i++)
7342 /* first check start_pos, then previous/next or (next/previous) pos */
7343 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7344 int pos = (pos_raw + 8) % 8;
7345 int newx = x + check_xy[pos].dx;
7346 int newy = y + check_xy[pos].dy;
7347 int new_move_dir = check_xy[pos].dir;
7349 if (IS_PLAYER(newx, newy))
7352 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7354 MovDir[x][y] = new_move_dir;
7355 MovDelay[x][y] = level.android_move_time * 8 + 1;
7362 else if (move_pattern == MV_TURNING_LEFT ||
7363 move_pattern == MV_TURNING_RIGHT ||
7364 move_pattern == MV_TURNING_LEFT_RIGHT ||
7365 move_pattern == MV_TURNING_RIGHT_LEFT ||
7366 move_pattern == MV_TURNING_RANDOM ||
7367 move_pattern == MV_ALL_DIRECTIONS)
7369 boolean can_turn_left =
7370 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7371 boolean can_turn_right =
7372 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7374 if (element_info[element].move_stepsize == 0) /* "not moving" */
7377 if (move_pattern == MV_TURNING_LEFT)
7378 MovDir[x][y] = left_dir;
7379 else if (move_pattern == MV_TURNING_RIGHT)
7380 MovDir[x][y] = right_dir;
7381 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7382 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7383 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7384 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7385 else if (move_pattern == MV_TURNING_RANDOM)
7386 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7387 can_turn_right && !can_turn_left ? right_dir :
7388 RND(2) ? left_dir : right_dir);
7389 else if (can_turn_left && can_turn_right)
7390 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7391 else if (can_turn_left)
7392 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7393 else if (can_turn_right)
7394 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7396 MovDir[x][y] = back_dir;
7398 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400 else if (move_pattern == MV_HORIZONTAL ||
7401 move_pattern == MV_VERTICAL)
7403 if (move_pattern & old_move_dir)
7404 MovDir[x][y] = back_dir;
7405 else if (move_pattern == MV_HORIZONTAL)
7406 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7407 else if (move_pattern == MV_VERTICAL)
7408 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7410 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7412 else if (move_pattern & MV_ANY_DIRECTION)
7414 MovDir[x][y] = move_pattern;
7415 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7417 else if (move_pattern & MV_WIND_DIRECTION)
7419 MovDir[x][y] = game.wind_direction;
7420 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7422 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7424 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7425 MovDir[x][y] = left_dir;
7426 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7427 MovDir[x][y] = right_dir;
7429 if (MovDir[x][y] != old_move_dir)
7430 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7432 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7434 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7435 MovDir[x][y] = right_dir;
7436 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7437 MovDir[x][y] = left_dir;
7439 if (MovDir[x][y] != old_move_dir)
7440 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7442 else if (move_pattern == MV_TOWARDS_PLAYER ||
7443 move_pattern == MV_AWAY_FROM_PLAYER)
7445 int attr_x = -1, attr_y = -1;
7447 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7458 for (i = 0; i < MAX_PLAYERS; i++)
7460 struct PlayerInfo *player = &stored_player[i];
7461 int jx = player->jx, jy = player->jy;
7463 if (!player->active)
7467 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7475 MovDir[x][y] = MV_NONE;
7477 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7478 else if (attr_x > x)
7479 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7481 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7482 else if (attr_y > y)
7483 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7485 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7487 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7489 boolean first_horiz = RND(2);
7490 int new_move_dir = MovDir[x][y];
7492 if (element_info[element].move_stepsize == 0) /* "not moving" */
7494 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7495 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7501 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7502 Moving2Blocked(x, y, &newx, &newy);
7504 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7508 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7509 Moving2Blocked(x, y, &newx, &newy);
7511 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7514 MovDir[x][y] = old_move_dir;
7517 else if (move_pattern == MV_WHEN_PUSHED ||
7518 move_pattern == MV_WHEN_DROPPED)
7520 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7521 MovDir[x][y] = MV_NONE;
7525 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7527 static int test_xy[7][2] =
7537 static int test_dir[7] =
7547 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7548 int move_preference = -1000000; /* start with very low preference */
7549 int new_move_dir = MV_NONE;
7550 int start_test = RND(4);
7553 for (i = 0; i < NUM_DIRECTIONS; i++)
7555 int move_dir = test_dir[start_test + i];
7556 int move_dir_preference;
7558 xx = x + test_xy[start_test + i][0];
7559 yy = y + test_xy[start_test + i][1];
7561 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7562 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7564 new_move_dir = move_dir;
7569 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7572 move_dir_preference = -1 * RunnerVisit[xx][yy];
7573 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7574 move_dir_preference = PlayerVisit[xx][yy];
7576 if (move_dir_preference > move_preference)
7578 /* prefer field that has not been visited for the longest time */
7579 move_preference = move_dir_preference;
7580 new_move_dir = move_dir;
7582 else if (move_dir_preference == move_preference &&
7583 move_dir == old_move_dir)
7585 /* prefer last direction when all directions are preferred equally */
7586 move_preference = move_dir_preference;
7587 new_move_dir = move_dir;
7591 MovDir[x][y] = new_move_dir;
7592 if (old_move_dir != new_move_dir)
7593 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7597 static void TurnRound(int x, int y)
7599 int direction = MovDir[x][y];
7603 GfxDir[x][y] = MovDir[x][y];
7605 if (direction != MovDir[x][y])
7609 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7611 ResetGfxFrame(x, y, FALSE);
7614 static boolean JustBeingPushed(int x, int y)
7618 for (i = 0; i < MAX_PLAYERS; i++)
7620 struct PlayerInfo *player = &stored_player[i];
7622 if (player->active && player->is_pushing && player->MovPos)
7624 int next_jx = player->jx + (player->jx - player->last_jx);
7625 int next_jy = player->jy + (player->jy - player->last_jy);
7627 if (x == next_jx && y == next_jy)
7635 void StartMoving(int x, int y)
7637 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7638 int element = Feld[x][y];
7643 if (MovDelay[x][y] == 0)
7644 GfxAction[x][y] = ACTION_DEFAULT;
7646 if (CAN_FALL(element) && y < lev_fieldy - 1)
7648 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7649 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7650 if (JustBeingPushed(x, y))
7653 if (element == EL_QUICKSAND_FULL)
7655 if (IS_FREE(x, y + 1))
7657 InitMovingField(x, y, MV_DOWN);
7658 started_moving = TRUE;
7660 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7661 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7662 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7663 Store[x][y] = EL_ROCK;
7665 Store[x][y] = EL_ROCK;
7668 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7670 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7672 if (!MovDelay[x][y])
7674 MovDelay[x][y] = TILEY + 1;
7676 ResetGfxAnimation(x, y);
7677 ResetGfxAnimation(x, y + 1);
7682 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7683 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7690 Feld[x][y] = EL_QUICKSAND_EMPTY;
7691 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7692 Store[x][y + 1] = Store[x][y];
7695 PlayLevelSoundAction(x, y, ACTION_FILLING);
7697 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_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_FAST_FILLING);
7717 Feld[x][y] = EL_QUICKSAND_EMPTY;
7718 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7719 Store[x][y + 1] = Store[x][y];
7722 PlayLevelSoundAction(x, y, ACTION_FILLING);
7725 else if (element == EL_QUICKSAND_FAST_FULL)
7727 if (IS_FREE(x, y + 1))
7729 InitMovingField(x, y, MV_DOWN);
7730 started_moving = TRUE;
7732 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7733 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7734 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7735 Store[x][y] = EL_ROCK;
7737 Store[x][y] = EL_ROCK;
7740 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7742 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7744 if (!MovDelay[x][y])
7746 MovDelay[x][y] = TILEY + 1;
7748 ResetGfxAnimation(x, y);
7749 ResetGfxAnimation(x, y + 1);
7754 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7755 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7762 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7763 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7764 Store[x][y + 1] = Store[x][y];
7767 PlayLevelSoundAction(x, y, ACTION_FILLING);
7769 else if (Feld[x][y + 1] == EL_QUICKSAND_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_FILLING);
7789 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7790 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7791 Store[x][y + 1] = Store[x][y];
7794 PlayLevelSoundAction(x, y, ACTION_FILLING);
7797 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7798 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7800 InitMovingField(x, y, MV_DOWN);
7801 started_moving = TRUE;
7803 Feld[x][y] = EL_QUICKSAND_FILLING;
7804 Store[x][y] = element;
7806 PlayLevelSoundAction(x, y, ACTION_FILLING);
7808 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7809 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7811 InitMovingField(x, y, MV_DOWN);
7812 started_moving = TRUE;
7814 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7815 Store[x][y] = element;
7817 PlayLevelSoundAction(x, y, ACTION_FILLING);
7819 else if (element == EL_MAGIC_WALL_FULL)
7821 if (IS_FREE(x, y + 1))
7823 InitMovingField(x, y, MV_DOWN);
7824 started_moving = TRUE;
7826 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7827 Store[x][y] = EL_CHANGED(Store[x][y]);
7829 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7831 if (!MovDelay[x][y])
7832 MovDelay[x][y] = TILEY/4 + 1;
7841 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7842 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7843 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7847 else if (element == EL_BD_MAGIC_WALL_FULL)
7849 if (IS_FREE(x, y + 1))
7851 InitMovingField(x, y, MV_DOWN);
7852 started_moving = TRUE;
7854 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7855 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7857 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7859 if (!MovDelay[x][y])
7860 MovDelay[x][y] = TILEY/4 + 1;
7869 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7870 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7871 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7875 else if (element == EL_DC_MAGIC_WALL_FULL)
7877 if (IS_FREE(x, y + 1))
7879 InitMovingField(x, y, MV_DOWN);
7880 started_moving = TRUE;
7882 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7883 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7885 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7887 if (!MovDelay[x][y])
7888 MovDelay[x][y] = TILEY/4 + 1;
7897 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7898 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7899 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7903 else if ((CAN_PASS_MAGIC_WALL(element) &&
7904 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7905 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7906 (CAN_PASS_DC_MAGIC_WALL(element) &&
7907 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7910 InitMovingField(x, y, MV_DOWN);
7911 started_moving = TRUE;
7914 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7915 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7916 EL_DC_MAGIC_WALL_FILLING);
7917 Store[x][y] = element;
7919 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7921 SplashAcid(x, y + 1);
7923 InitMovingField(x, y, MV_DOWN);
7924 started_moving = TRUE;
7926 Store[x][y] = EL_ACID;
7929 #if USE_FIX_IMPACT_COLLISION
7930 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7931 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7933 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7934 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7936 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7937 CAN_FALL(element) && WasJustFalling[x][y] &&
7938 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7940 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7941 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7942 (Feld[x][y + 1] == EL_BLOCKED)))
7944 /* this is needed for a special case not covered by calling "Impact()"
7945 from "ContinueMoving()": if an element moves to a tile directly below
7946 another element which was just falling on that tile (which was empty
7947 in the previous frame), the falling element above would just stop
7948 instead of smashing the element below (in previous version, the above
7949 element was just checked for "moving" instead of "falling", resulting
7950 in incorrect smashes caused by horizontal movement of the above
7951 element; also, the case of the player being the element to smash was
7952 simply not covered here... :-/ ) */
7954 CheckCollision[x][y] = 0;
7955 CheckImpact[x][y] = 0;
7959 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7961 if (MovDir[x][y] == MV_NONE)
7963 InitMovingField(x, y, MV_DOWN);
7964 started_moving = TRUE;
7967 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7969 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7970 MovDir[x][y] = MV_DOWN;
7972 InitMovingField(x, y, MV_DOWN);
7973 started_moving = TRUE;
7975 else if (element == EL_AMOEBA_DROP)
7977 Feld[x][y] = EL_AMOEBA_GROWING;
7978 Store[x][y] = EL_AMOEBA_WET;
7980 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7981 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7982 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7983 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7985 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7986 (IS_FREE(x - 1, y + 1) ||
7987 Feld[x - 1][y + 1] == EL_ACID));
7988 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7989 (IS_FREE(x + 1, y + 1) ||
7990 Feld[x + 1][y + 1] == EL_ACID));
7991 boolean can_fall_any = (can_fall_left || can_fall_right);
7992 boolean can_fall_both = (can_fall_left && can_fall_right);
7993 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7995 #if USE_NEW_ALL_SLIPPERY
7996 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7998 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7999 can_fall_right = FALSE;
8000 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8001 can_fall_left = FALSE;
8002 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8003 can_fall_right = FALSE;
8004 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8005 can_fall_left = FALSE;
8007 can_fall_any = (can_fall_left || can_fall_right);
8008 can_fall_both = FALSE;
8011 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8013 if (slippery_type == SLIPPERY_ONLY_LEFT)
8014 can_fall_right = FALSE;
8015 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8016 can_fall_left = FALSE;
8017 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8018 can_fall_right = FALSE;
8019 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8020 can_fall_left = FALSE;
8022 can_fall_any = (can_fall_left || can_fall_right);
8023 can_fall_both = (can_fall_left && can_fall_right);
8027 #if USE_NEW_ALL_SLIPPERY
8029 #if USE_NEW_SP_SLIPPERY
8030 /* !!! better use the same properties as for custom elements here !!! */
8031 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8032 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8034 can_fall_right = FALSE; /* slip down on left side */
8035 can_fall_both = FALSE;
8040 #if USE_NEW_ALL_SLIPPERY
8043 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8044 can_fall_right = FALSE; /* slip down on left side */
8046 can_fall_left = !(can_fall_right = RND(2));
8048 can_fall_both = FALSE;
8053 if (game.emulation == EMU_BOULDERDASH ||
8054 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8055 can_fall_right = FALSE; /* slip down on left side */
8057 can_fall_left = !(can_fall_right = RND(2));
8059 can_fall_both = FALSE;
8065 /* if not determined otherwise, prefer left side for slipping down */
8066 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8067 started_moving = TRUE;
8071 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8073 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8076 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8077 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8078 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8079 int belt_dir = game.belt_dir[belt_nr];
8081 if ((belt_dir == MV_LEFT && left_is_free) ||
8082 (belt_dir == MV_RIGHT && right_is_free))
8084 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8086 InitMovingField(x, y, belt_dir);
8087 started_moving = TRUE;
8089 Pushed[x][y] = TRUE;
8090 Pushed[nextx][y] = TRUE;
8092 GfxAction[x][y] = ACTION_DEFAULT;
8096 MovDir[x][y] = 0; /* if element was moving, stop it */
8101 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8103 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8105 if (CAN_MOVE(element) && !started_moving)
8108 int move_pattern = element_info[element].move_pattern;
8113 if (MovDir[x][y] == MV_NONE)
8115 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8116 x, y, element, element_info[element].token_name);
8117 printf("StartMoving(): This should never happen!\n");
8122 Moving2Blocked(x, y, &newx, &newy);
8124 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8127 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8128 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8130 WasJustMoving[x][y] = 0;
8131 CheckCollision[x][y] = 0;
8133 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8135 if (Feld[x][y] != element) /* element has changed */
8139 if (!MovDelay[x][y]) /* start new movement phase */
8141 /* all objects that can change their move direction after each step
8142 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8144 if (element != EL_YAMYAM &&
8145 element != EL_DARK_YAMYAM &&
8146 element != EL_PACMAN &&
8147 !(move_pattern & MV_ANY_DIRECTION) &&
8148 move_pattern != MV_TURNING_LEFT &&
8149 move_pattern != MV_TURNING_RIGHT &&
8150 move_pattern != MV_TURNING_LEFT_RIGHT &&
8151 move_pattern != MV_TURNING_RIGHT_LEFT &&
8152 move_pattern != MV_TURNING_RANDOM)
8156 if (MovDelay[x][y] && (element == EL_BUG ||
8157 element == EL_SPACESHIP ||
8158 element == EL_SP_SNIKSNAK ||
8159 element == EL_SP_ELECTRON ||
8160 element == EL_MOLE))
8161 TEST_DrawLevelField(x, y);
8165 if (MovDelay[x][y]) /* wait some time before next movement */
8169 if (element == EL_ROBOT ||
8170 element == EL_YAMYAM ||
8171 element == EL_DARK_YAMYAM)
8173 DrawLevelElementAnimationIfNeeded(x, y, element);
8174 PlayLevelSoundAction(x, y, ACTION_WAITING);
8176 else if (element == EL_SP_ELECTRON)
8177 DrawLevelElementAnimationIfNeeded(x, y, element);
8178 else if (element == EL_DRAGON)
8181 int dir = MovDir[x][y];
8182 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8183 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8184 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8185 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8186 dir == MV_UP ? IMG_FLAMES_1_UP :
8187 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8188 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8190 GfxAction[x][y] = ACTION_ATTACKING;
8192 if (IS_PLAYER(x, y))
8193 DrawPlayerField(x, y);
8195 TEST_DrawLevelField(x, y);
8197 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8199 for (i = 1; i <= 3; i++)
8201 int xx = x + i * dx;
8202 int yy = y + i * dy;
8203 int sx = SCREENX(xx);
8204 int sy = SCREENY(yy);
8205 int flame_graphic = graphic + (i - 1);
8207 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8212 int flamed = MovingOrBlocked2Element(xx, yy);
8216 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8218 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8219 RemoveMovingField(xx, yy);
8221 RemoveField(xx, yy);
8223 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8226 RemoveMovingField(xx, yy);
8229 ChangeDelay[xx][yy] = 0;
8231 Feld[xx][yy] = EL_FLAMES;
8233 if (IN_SCR_FIELD(sx, sy))
8235 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8236 DrawGraphic(sx, sy, flame_graphic, frame);
8241 if (Feld[xx][yy] == EL_FLAMES)
8242 Feld[xx][yy] = EL_EMPTY;
8243 TEST_DrawLevelField(xx, yy);
8248 if (MovDelay[x][y]) /* element still has to wait some time */
8250 PlayLevelSoundAction(x, y, ACTION_WAITING);
8256 /* now make next step */
8258 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8260 if (DONT_COLLIDE_WITH(element) &&
8261 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8262 !PLAYER_ENEMY_PROTECTED(newx, newy))
8264 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8269 else if (CAN_MOVE_INTO_ACID(element) &&
8270 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8271 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8272 (MovDir[x][y] == MV_DOWN ||
8273 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8275 SplashAcid(newx, newy);
8276 Store[x][y] = EL_ACID;
8278 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8280 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8281 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8282 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8283 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8286 TEST_DrawLevelField(x, y);
8288 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8289 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8290 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8292 local_player->friends_still_needed--;
8293 if (!local_player->friends_still_needed &&
8294 !local_player->GameOver && AllPlayersGone)
8295 PlayerWins(local_player);
8299 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8301 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8302 TEST_DrawLevelField(newx, newy);
8304 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8306 else if (!IS_FREE(newx, newy))
8308 GfxAction[x][y] = ACTION_WAITING;
8310 if (IS_PLAYER(x, y))
8311 DrawPlayerField(x, y);
8313 TEST_DrawLevelField(x, y);
8318 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8320 if (IS_FOOD_PIG(Feld[newx][newy]))
8322 if (IS_MOVING(newx, newy))
8323 RemoveMovingField(newx, newy);
8326 Feld[newx][newy] = EL_EMPTY;
8327 TEST_DrawLevelField(newx, newy);
8330 PlayLevelSound(x, y, SND_PIG_DIGGING);
8332 else if (!IS_FREE(newx, newy))
8334 if (IS_PLAYER(x, y))
8335 DrawPlayerField(x, y);
8337 TEST_DrawLevelField(x, y);
8342 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8344 if (Store[x][y] != EL_EMPTY)
8346 boolean can_clone = FALSE;
8349 /* check if element to clone is still there */
8350 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8352 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8360 /* cannot clone or target field not free anymore -- do not clone */
8361 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8362 Store[x][y] = EL_EMPTY;
8365 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8367 if (IS_MV_DIAGONAL(MovDir[x][y]))
8369 int diagonal_move_dir = MovDir[x][y];
8370 int stored = Store[x][y];
8371 int change_delay = 8;
8374 /* android is moving diagonally */
8376 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8378 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8379 GfxElement[x][y] = EL_EMC_ANDROID;
8380 GfxAction[x][y] = ACTION_SHRINKING;
8381 GfxDir[x][y] = diagonal_move_dir;
8382 ChangeDelay[x][y] = change_delay;
8384 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8387 DrawLevelGraphicAnimation(x, y, graphic);
8388 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8390 if (Feld[newx][newy] == EL_ACID)
8392 SplashAcid(newx, newy);
8397 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8399 Store[newx][newy] = EL_EMC_ANDROID;
8400 GfxElement[newx][newy] = EL_EMC_ANDROID;
8401 GfxAction[newx][newy] = ACTION_GROWING;
8402 GfxDir[newx][newy] = diagonal_move_dir;
8403 ChangeDelay[newx][newy] = change_delay;
8405 graphic = el_act_dir2img(GfxElement[newx][newy],
8406 GfxAction[newx][newy], GfxDir[newx][newy]);
8408 DrawLevelGraphicAnimation(newx, newy, graphic);
8409 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8415 Feld[newx][newy] = EL_EMPTY;
8416 TEST_DrawLevelField(newx, newy);
8418 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8421 else if (!IS_FREE(newx, newy))
8424 if (IS_PLAYER(x, y))
8425 DrawPlayerField(x, y);
8427 TEST_DrawLevelField(x, y);
8433 else if (IS_CUSTOM_ELEMENT(element) &&
8434 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8437 if (!DigFieldByCE(newx, newy, element))
8440 int new_element = Feld[newx][newy];
8442 if (!IS_FREE(newx, newy))
8444 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8445 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8448 /* no element can dig solid indestructible elements */
8449 if (IS_INDESTRUCTIBLE(new_element) &&
8450 !IS_DIGGABLE(new_element) &&
8451 !IS_COLLECTIBLE(new_element))
8454 if (AmoebaNr[newx][newy] &&
8455 (new_element == EL_AMOEBA_FULL ||
8456 new_element == EL_BD_AMOEBA ||
8457 new_element == EL_AMOEBA_GROWING))
8459 AmoebaCnt[AmoebaNr[newx][newy]]--;
8460 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8463 if (IS_MOVING(newx, newy))
8464 RemoveMovingField(newx, newy);
8467 RemoveField(newx, newy);
8468 TEST_DrawLevelField(newx, newy);
8471 /* if digged element was about to explode, prevent the explosion */
8472 ExplodeField[newx][newy] = EX_TYPE_NONE;
8474 PlayLevelSoundAction(x, y, action);
8477 Store[newx][newy] = EL_EMPTY;
8480 /* this makes it possible to leave the removed element again */
8481 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8482 Store[newx][newy] = new_element;
8484 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8486 int move_leave_element = element_info[element].move_leave_element;
8488 /* this makes it possible to leave the removed element again */
8489 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8490 new_element : move_leave_element);
8496 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8498 RunnerVisit[x][y] = FrameCounter;
8499 PlayerVisit[x][y] /= 8; /* expire player visit path */
8502 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8504 if (!IS_FREE(newx, newy))
8506 if (IS_PLAYER(x, y))
8507 DrawPlayerField(x, y);
8509 TEST_DrawLevelField(x, y);
8515 boolean wanna_flame = !RND(10);
8516 int dx = newx - x, dy = newy - y;
8517 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8518 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8519 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8520 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8521 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8522 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8525 IS_CLASSIC_ENEMY(element1) ||
8526 IS_CLASSIC_ENEMY(element2)) &&
8527 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8528 element1 != EL_FLAMES && element2 != EL_FLAMES)
8530 ResetGfxAnimation(x, y);
8531 GfxAction[x][y] = ACTION_ATTACKING;
8533 if (IS_PLAYER(x, y))
8534 DrawPlayerField(x, y);
8536 TEST_DrawLevelField(x, y);
8538 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8540 MovDelay[x][y] = 50;
8544 RemoveField(newx, newy);
8546 Feld[newx][newy] = EL_FLAMES;
8547 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8550 RemoveField(newx1, newy1);
8552 Feld[newx1][newy1] = EL_FLAMES;
8554 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8557 RemoveField(newx2, newy2);
8559 Feld[newx2][newy2] = EL_FLAMES;
8566 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8567 Feld[newx][newy] == EL_DIAMOND)
8569 if (IS_MOVING(newx, newy))
8570 RemoveMovingField(newx, newy);
8573 Feld[newx][newy] = EL_EMPTY;
8574 TEST_DrawLevelField(newx, newy);
8577 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8579 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8580 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8582 if (AmoebaNr[newx][newy])
8584 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8585 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8586 Feld[newx][newy] == EL_BD_AMOEBA)
8587 AmoebaCnt[AmoebaNr[newx][newy]]--;
8592 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8594 RemoveMovingField(newx, newy);
8597 if (IS_MOVING(newx, newy))
8599 RemoveMovingField(newx, newy);
8604 Feld[newx][newy] = EL_EMPTY;
8605 TEST_DrawLevelField(newx, newy);
8608 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8610 else if ((element == EL_PACMAN || element == EL_MOLE)
8611 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8613 if (AmoebaNr[newx][newy])
8615 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8616 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8617 Feld[newx][newy] == EL_BD_AMOEBA)
8618 AmoebaCnt[AmoebaNr[newx][newy]]--;
8621 if (element == EL_MOLE)
8623 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8624 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8626 ResetGfxAnimation(x, y);
8627 GfxAction[x][y] = ACTION_DIGGING;
8628 TEST_DrawLevelField(x, y);
8630 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8632 return; /* wait for shrinking amoeba */
8634 else /* element == EL_PACMAN */
8636 Feld[newx][newy] = EL_EMPTY;
8637 TEST_DrawLevelField(newx, newy);
8638 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8641 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8642 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8643 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8645 /* wait for shrinking amoeba to completely disappear */
8648 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8650 /* object was running against a wall */
8655 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8656 if (move_pattern & MV_ANY_DIRECTION &&
8657 move_pattern == MovDir[x][y])
8659 int blocking_element =
8660 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8662 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8665 element = Feld[x][y]; /* element might have changed */
8669 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8670 DrawLevelElementAnimation(x, y, element);
8672 if (DONT_TOUCH(element))
8673 TestIfBadThingTouchesPlayer(x, y);
8678 InitMovingField(x, y, MovDir[x][y]);
8680 PlayLevelSoundAction(x, y, ACTION_MOVING);
8684 ContinueMoving(x, y);
8687 void ContinueMoving(int x, int y)
8689 int element = Feld[x][y];
8690 struct ElementInfo *ei = &element_info[element];
8691 int direction = MovDir[x][y];
8692 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8693 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8694 int newx = x + dx, newy = y + dy;
8695 int stored = Store[x][y];
8696 int stored_new = Store[newx][newy];
8697 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8698 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8699 boolean last_line = (newy == lev_fieldy - 1);
8701 MovPos[x][y] += getElementMoveStepsize(x, y);
8703 if (pushed_by_player) /* special case: moving object pushed by player */
8704 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8706 if (ABS(MovPos[x][y]) < TILEX)
8709 int ee = Feld[x][y];
8710 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8711 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8713 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8714 x, y, ABS(MovPos[x][y]),
8716 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8719 TEST_DrawLevelField(x, y);
8721 return; /* element is still moving */
8724 /* element reached destination field */
8726 Feld[x][y] = EL_EMPTY;
8727 Feld[newx][newy] = element;
8728 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8730 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8732 element = Feld[newx][newy] = EL_ACID;
8734 else if (element == EL_MOLE)
8736 Feld[x][y] = EL_SAND;
8738 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8740 else if (element == EL_QUICKSAND_FILLING)
8742 element = Feld[newx][newy] = get_next_element(element);
8743 Store[newx][newy] = Store[x][y];
8745 else if (element == EL_QUICKSAND_EMPTYING)
8747 Feld[x][y] = get_next_element(element);
8748 element = Feld[newx][newy] = Store[x][y];
8750 else if (element == EL_QUICKSAND_FAST_FILLING)
8752 element = Feld[newx][newy] = get_next_element(element);
8753 Store[newx][newy] = Store[x][y];
8755 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8757 Feld[x][y] = get_next_element(element);
8758 element = Feld[newx][newy] = Store[x][y];
8760 else if (element == EL_MAGIC_WALL_FILLING)
8762 element = Feld[newx][newy] = get_next_element(element);
8763 if (!game.magic_wall_active)
8764 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8765 Store[newx][newy] = Store[x][y];
8767 else if (element == EL_MAGIC_WALL_EMPTYING)
8769 Feld[x][y] = get_next_element(element);
8770 if (!game.magic_wall_active)
8771 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8772 element = Feld[newx][newy] = Store[x][y];
8774 #if USE_NEW_CUSTOM_VALUE
8775 InitField(newx, newy, FALSE);
8778 else if (element == EL_BD_MAGIC_WALL_FILLING)
8780 element = Feld[newx][newy] = get_next_element(element);
8781 if (!game.magic_wall_active)
8782 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8783 Store[newx][newy] = Store[x][y];
8785 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8787 Feld[x][y] = get_next_element(element);
8788 if (!game.magic_wall_active)
8789 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8790 element = Feld[newx][newy] = Store[x][y];
8792 #if USE_NEW_CUSTOM_VALUE
8793 InitField(newx, newy, FALSE);
8796 else if (element == EL_DC_MAGIC_WALL_FILLING)
8798 element = Feld[newx][newy] = get_next_element(element);
8799 if (!game.magic_wall_active)
8800 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8801 Store[newx][newy] = Store[x][y];
8803 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8805 Feld[x][y] = get_next_element(element);
8806 if (!game.magic_wall_active)
8807 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8808 element = Feld[newx][newy] = Store[x][y];
8810 #if USE_NEW_CUSTOM_VALUE
8811 InitField(newx, newy, FALSE);
8814 else if (element == EL_AMOEBA_DROPPING)
8816 Feld[x][y] = get_next_element(element);
8817 element = Feld[newx][newy] = Store[x][y];
8819 else if (element == EL_SOKOBAN_OBJECT)
8822 Feld[x][y] = Back[x][y];
8824 if (Back[newx][newy])
8825 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8827 Back[x][y] = Back[newx][newy] = 0;
8830 Store[x][y] = EL_EMPTY;
8835 MovDelay[newx][newy] = 0;
8837 if (CAN_CHANGE_OR_HAS_ACTION(element))
8839 /* copy element change control values to new field */
8840 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8841 ChangePage[newx][newy] = ChangePage[x][y];
8842 ChangeCount[newx][newy] = ChangeCount[x][y];
8843 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8846 #if USE_NEW_CUSTOM_VALUE
8847 CustomValue[newx][newy] = CustomValue[x][y];
8850 ChangeDelay[x][y] = 0;
8851 ChangePage[x][y] = -1;
8852 ChangeCount[x][y] = 0;
8853 ChangeEvent[x][y] = -1;
8855 #if USE_NEW_CUSTOM_VALUE
8856 CustomValue[x][y] = 0;
8859 /* copy animation control values to new field */
8860 GfxFrame[newx][newy] = GfxFrame[x][y];
8861 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8862 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8863 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8865 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8867 /* some elements can leave other elements behind after moving */
8869 if (ei->move_leave_element != EL_EMPTY &&
8870 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8871 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8873 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8874 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8875 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8878 int move_leave_element = ei->move_leave_element;
8882 /* this makes it possible to leave the removed element again */
8883 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8884 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8886 /* this makes it possible to leave the removed element again */
8887 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8888 move_leave_element = stored;
8891 /* this makes it possible to leave the removed element again */
8892 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8893 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8894 move_leave_element = stored;
8897 Feld[x][y] = move_leave_element;
8899 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8900 MovDir[x][y] = direction;
8902 InitField(x, y, FALSE);
8904 if (GFX_CRUMBLED(Feld[x][y]))
8905 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8907 if (ELEM_IS_PLAYER(move_leave_element))
8908 RelocatePlayer(x, y, move_leave_element);
8911 /* do this after checking for left-behind element */
8912 ResetGfxAnimation(x, y); /* reset animation values for old field */
8914 if (!CAN_MOVE(element) ||
8915 (CAN_FALL(element) && direction == MV_DOWN &&
8916 (element == EL_SPRING ||
8917 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8918 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8919 GfxDir[x][y] = MovDir[newx][newy] = 0;
8921 TEST_DrawLevelField(x, y);
8922 TEST_DrawLevelField(newx, newy);
8924 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8926 /* prevent pushed element from moving on in pushed direction */
8927 if (pushed_by_player && CAN_MOVE(element) &&
8928 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8929 !(element_info[element].move_pattern & direction))
8930 TurnRound(newx, newy);
8932 /* prevent elements on conveyor belt from moving on in last direction */
8933 if (pushed_by_conveyor && CAN_FALL(element) &&
8934 direction & MV_HORIZONTAL)
8935 MovDir[newx][newy] = 0;
8937 if (!pushed_by_player)
8939 int nextx = newx + dx, nexty = newy + dy;
8940 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8942 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8944 if (CAN_FALL(element) && direction == MV_DOWN)
8945 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8947 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8948 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8950 #if USE_FIX_IMPACT_COLLISION
8951 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8952 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8956 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8958 TestIfBadThingTouchesPlayer(newx, newy);
8959 TestIfBadThingTouchesFriend(newx, newy);
8961 if (!IS_CUSTOM_ELEMENT(element))
8962 TestIfBadThingTouchesOtherBadThing(newx, newy);
8964 else if (element == EL_PENGUIN)
8965 TestIfFriendTouchesBadThing(newx, newy);
8967 if (DONT_GET_HIT_BY(element))
8969 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8972 /* give the player one last chance (one more frame) to move away */
8973 if (CAN_FALL(element) && direction == MV_DOWN &&
8974 (last_line || (!IS_FREE(x, newy + 1) &&
8975 (!IS_PLAYER(x, newy + 1) ||
8976 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8979 if (pushed_by_player && !game.use_change_when_pushing_bug)
8981 int push_side = MV_DIR_OPPOSITE(direction);
8982 struct PlayerInfo *player = PLAYERINFO(x, y);
8984 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8985 player->index_bit, push_side);
8986 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8987 player->index_bit, push_side);
8990 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8991 MovDelay[newx][newy] = 1;
8993 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8995 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8998 if (ChangePage[newx][newy] != -1) /* delayed change */
9000 int page = ChangePage[newx][newy];
9001 struct ElementChangeInfo *change = &ei->change_page[page];
9003 ChangePage[newx][newy] = -1;
9005 if (change->can_change)
9007 if (ChangeElement(newx, newy, element, page))
9009 if (change->post_change_function)
9010 change->post_change_function(newx, newy);
9014 if (change->has_action)
9015 ExecuteCustomElementAction(newx, newy, element, page);
9019 TestIfElementHitsCustomElement(newx, newy, direction);
9020 TestIfPlayerTouchesCustomElement(newx, newy);
9021 TestIfElementTouchesCustomElement(newx, newy);
9023 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9024 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9025 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9026 MV_DIR_OPPOSITE(direction));
9029 int AmoebeNachbarNr(int ax, int ay)
9032 int element = Feld[ax][ay];
9034 static int xy[4][2] =
9042 for (i = 0; i < NUM_DIRECTIONS; i++)
9044 int x = ax + xy[i][0];
9045 int y = ay + xy[i][1];
9047 if (!IN_LEV_FIELD(x, y))
9050 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9051 group_nr = AmoebaNr[x][y];
9057 void AmoebenVereinigen(int ax, int ay)
9059 int i, x, y, xx, yy;
9060 int new_group_nr = AmoebaNr[ax][ay];
9061 static int xy[4][2] =
9069 if (new_group_nr == 0)
9072 for (i = 0; i < NUM_DIRECTIONS; i++)
9077 if (!IN_LEV_FIELD(x, y))
9080 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9081 Feld[x][y] == EL_BD_AMOEBA ||
9082 Feld[x][y] == EL_AMOEBA_DEAD) &&
9083 AmoebaNr[x][y] != new_group_nr)
9085 int old_group_nr = AmoebaNr[x][y];
9087 if (old_group_nr == 0)
9090 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9091 AmoebaCnt[old_group_nr] = 0;
9092 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9093 AmoebaCnt2[old_group_nr] = 0;
9095 SCAN_PLAYFIELD(xx, yy)
9097 if (AmoebaNr[xx][yy] == old_group_nr)
9098 AmoebaNr[xx][yy] = new_group_nr;
9104 void AmoebeUmwandeln(int ax, int ay)
9108 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9110 int group_nr = AmoebaNr[ax][ay];
9115 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9116 printf("AmoebeUmwandeln(): This should never happen!\n");
9121 SCAN_PLAYFIELD(x, y)
9123 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9126 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9130 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9131 SND_AMOEBA_TURNING_TO_GEM :
9132 SND_AMOEBA_TURNING_TO_ROCK));
9137 static int xy[4][2] =
9145 for (i = 0; i < NUM_DIRECTIONS; i++)
9150 if (!IN_LEV_FIELD(x, y))
9153 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9155 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9156 SND_AMOEBA_TURNING_TO_GEM :
9157 SND_AMOEBA_TURNING_TO_ROCK));
9164 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9167 int group_nr = AmoebaNr[ax][ay];
9168 boolean done = FALSE;
9173 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9174 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9179 SCAN_PLAYFIELD(x, y)
9181 if (AmoebaNr[x][y] == group_nr &&
9182 (Feld[x][y] == EL_AMOEBA_DEAD ||
9183 Feld[x][y] == EL_BD_AMOEBA ||
9184 Feld[x][y] == EL_AMOEBA_GROWING))
9187 Feld[x][y] = new_element;
9188 InitField(x, y, FALSE);
9189 TEST_DrawLevelField(x, y);
9195 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9196 SND_BD_AMOEBA_TURNING_TO_ROCK :
9197 SND_BD_AMOEBA_TURNING_TO_GEM));
9200 void AmoebeWaechst(int x, int y)
9202 static unsigned long sound_delay = 0;
9203 static unsigned long sound_delay_value = 0;
9205 if (!MovDelay[x][y]) /* start new growing cycle */
9209 if (DelayReached(&sound_delay, sound_delay_value))
9211 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9212 sound_delay_value = 30;
9216 if (MovDelay[x][y]) /* wait some time before growing bigger */
9219 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9221 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9222 6 - MovDelay[x][y]);
9224 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9227 if (!MovDelay[x][y])
9229 Feld[x][y] = Store[x][y];
9231 TEST_DrawLevelField(x, y);
9236 void AmoebaDisappearing(int x, int y)
9238 static unsigned long sound_delay = 0;
9239 static unsigned long sound_delay_value = 0;
9241 if (!MovDelay[x][y]) /* start new shrinking cycle */
9245 if (DelayReached(&sound_delay, sound_delay_value))
9246 sound_delay_value = 30;
9249 if (MovDelay[x][y]) /* wait some time before shrinking */
9252 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9254 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9255 6 - MovDelay[x][y]);
9257 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9260 if (!MovDelay[x][y])
9262 Feld[x][y] = EL_EMPTY;
9263 TEST_DrawLevelField(x, y);
9265 /* don't let mole enter this field in this cycle;
9266 (give priority to objects falling to this field from above) */
9272 void AmoebeAbleger(int ax, int ay)
9275 int element = Feld[ax][ay];
9276 int graphic = el2img(element);
9277 int newax = ax, neway = ay;
9278 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9279 static int xy[4][2] =
9287 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9289 Feld[ax][ay] = EL_AMOEBA_DEAD;
9290 TEST_DrawLevelField(ax, ay);
9294 if (IS_ANIMATED(graphic))
9295 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9297 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9298 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9300 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9303 if (MovDelay[ax][ay])
9307 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9310 int x = ax + xy[start][0];
9311 int y = ay + xy[start][1];
9313 if (!IN_LEV_FIELD(x, y))
9316 if (IS_FREE(x, y) ||
9317 CAN_GROW_INTO(Feld[x][y]) ||
9318 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9319 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9325 if (newax == ax && neway == ay)
9328 else /* normal or "filled" (BD style) amoeba */
9331 boolean waiting_for_player = FALSE;
9333 for (i = 0; i < NUM_DIRECTIONS; i++)
9335 int j = (start + i) % 4;
9336 int x = ax + xy[j][0];
9337 int y = ay + xy[j][1];
9339 if (!IN_LEV_FIELD(x, y))
9342 if (IS_FREE(x, y) ||
9343 CAN_GROW_INTO(Feld[x][y]) ||
9344 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9345 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9351 else if (IS_PLAYER(x, y))
9352 waiting_for_player = TRUE;
9355 if (newax == ax && neway == ay) /* amoeba cannot grow */
9357 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9359 Feld[ax][ay] = EL_AMOEBA_DEAD;
9360 TEST_DrawLevelField(ax, ay);
9361 AmoebaCnt[AmoebaNr[ax][ay]]--;
9363 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9365 if (element == EL_AMOEBA_FULL)
9366 AmoebeUmwandeln(ax, ay);
9367 else if (element == EL_BD_AMOEBA)
9368 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9373 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9375 /* amoeba gets larger by growing in some direction */
9377 int new_group_nr = AmoebaNr[ax][ay];
9380 if (new_group_nr == 0)
9382 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9383 printf("AmoebeAbleger(): This should never happen!\n");
9388 AmoebaNr[newax][neway] = new_group_nr;
9389 AmoebaCnt[new_group_nr]++;
9390 AmoebaCnt2[new_group_nr]++;
9392 /* if amoeba touches other amoeba(s) after growing, unify them */
9393 AmoebenVereinigen(newax, neway);
9395 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9397 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9403 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9404 (neway == lev_fieldy - 1 && newax != ax))
9406 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9407 Store[newax][neway] = element;
9409 else if (neway == ay || element == EL_EMC_DRIPPER)
9411 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9413 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9417 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9418 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9419 Store[ax][ay] = EL_AMOEBA_DROP;
9420 ContinueMoving(ax, ay);
9424 TEST_DrawLevelField(newax, neway);
9427 void Life(int ax, int ay)
9431 int element = Feld[ax][ay];
9432 int graphic = el2img(element);
9433 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9435 boolean changed = FALSE;
9437 if (IS_ANIMATED(graphic))
9438 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9443 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9444 MovDelay[ax][ay] = life_time;
9446 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9449 if (MovDelay[ax][ay])
9453 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9455 int xx = ax+x1, yy = ay+y1;
9458 if (!IN_LEV_FIELD(xx, yy))
9461 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9463 int x = xx+x2, y = yy+y2;
9465 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9468 if (((Feld[x][y] == element ||
9469 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9471 (IS_FREE(x, y) && Stop[x][y]))
9475 if (xx == ax && yy == ay) /* field in the middle */
9477 if (nachbarn < life_parameter[0] ||
9478 nachbarn > life_parameter[1])
9480 Feld[xx][yy] = EL_EMPTY;
9482 TEST_DrawLevelField(xx, yy);
9483 Stop[xx][yy] = TRUE;
9487 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9488 { /* free border field */
9489 if (nachbarn >= life_parameter[2] &&
9490 nachbarn <= life_parameter[3])
9492 Feld[xx][yy] = element;
9493 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9495 TEST_DrawLevelField(xx, yy);
9496 Stop[xx][yy] = TRUE;
9503 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9504 SND_GAME_OF_LIFE_GROWING);
9507 static void InitRobotWheel(int x, int y)
9509 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9512 static void RunRobotWheel(int x, int y)
9514 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9517 static void StopRobotWheel(int x, int y)
9519 if (ZX == x && ZY == y)
9523 game.robot_wheel_active = FALSE;
9527 static void InitTimegateWheel(int x, int y)
9529 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9532 static void RunTimegateWheel(int x, int y)
9534 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9537 static void InitMagicBallDelay(int x, int y)
9540 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9542 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9546 static void ActivateMagicBall(int bx, int by)
9550 if (level.ball_random)
9552 int pos_border = RND(8); /* select one of the eight border elements */
9553 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9554 int xx = pos_content % 3;
9555 int yy = pos_content / 3;
9560 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9561 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9565 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9567 int xx = x - bx + 1;
9568 int yy = y - by + 1;
9570 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9571 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9575 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9578 void CheckExit(int x, int y)
9580 if (local_player->gems_still_needed > 0 ||
9581 local_player->sokobanfields_still_needed > 0 ||
9582 local_player->lights_still_needed > 0)
9584 int element = Feld[x][y];
9585 int graphic = el2img(element);
9587 if (IS_ANIMATED(graphic))
9588 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9593 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9596 Feld[x][y] = EL_EXIT_OPENING;
9598 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9601 void CheckExitEM(int x, int y)
9603 if (local_player->gems_still_needed > 0 ||
9604 local_player->sokobanfields_still_needed > 0 ||
9605 local_player->lights_still_needed > 0)
9607 int element = Feld[x][y];
9608 int graphic = el2img(element);
9610 if (IS_ANIMATED(graphic))
9611 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9616 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9619 Feld[x][y] = EL_EM_EXIT_OPENING;
9621 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9624 void CheckExitSteel(int x, int y)
9626 if (local_player->gems_still_needed > 0 ||
9627 local_player->sokobanfields_still_needed > 0 ||
9628 local_player->lights_still_needed > 0)
9630 int element = Feld[x][y];
9631 int graphic = el2img(element);
9633 if (IS_ANIMATED(graphic))
9634 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9639 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9642 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9644 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9647 void CheckExitSteelEM(int x, int y)
9649 if (local_player->gems_still_needed > 0 ||
9650 local_player->sokobanfields_still_needed > 0 ||
9651 local_player->lights_still_needed > 0)
9653 int element = Feld[x][y];
9654 int graphic = el2img(element);
9656 if (IS_ANIMATED(graphic))
9657 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9662 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9665 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9667 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9670 void CheckExitSP(int x, int y)
9672 if (local_player->gems_still_needed > 0)
9674 int element = Feld[x][y];
9675 int graphic = el2img(element);
9677 if (IS_ANIMATED(graphic))
9678 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9683 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9686 Feld[x][y] = EL_SP_EXIT_OPENING;
9688 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9691 static void CloseAllOpenTimegates()
9695 SCAN_PLAYFIELD(x, y)
9697 int element = Feld[x][y];
9699 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9701 Feld[x][y] = EL_TIMEGATE_CLOSING;
9703 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9708 void DrawTwinkleOnField(int x, int y)
9710 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9713 if (Feld[x][y] == EL_BD_DIAMOND)
9716 if (MovDelay[x][y] == 0) /* next animation frame */
9717 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9719 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9723 DrawLevelElementAnimation(x, y, Feld[x][y]);
9725 if (MovDelay[x][y] != 0)
9727 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9728 10 - MovDelay[x][y]);
9730 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9735 void MauerWaechst(int x, int y)
9739 if (!MovDelay[x][y]) /* next animation frame */
9740 MovDelay[x][y] = 3 * delay;
9742 if (MovDelay[x][y]) /* wait some time before next frame */
9746 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9748 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9749 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9751 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9754 if (!MovDelay[x][y])
9756 if (MovDir[x][y] == MV_LEFT)
9758 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9759 TEST_DrawLevelField(x - 1, y);
9761 else if (MovDir[x][y] == MV_RIGHT)
9763 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9764 TEST_DrawLevelField(x + 1, y);
9766 else if (MovDir[x][y] == MV_UP)
9768 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9769 TEST_DrawLevelField(x, y - 1);
9773 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9774 TEST_DrawLevelField(x, y + 1);
9777 Feld[x][y] = Store[x][y];
9779 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9780 TEST_DrawLevelField(x, y);
9785 void MauerAbleger(int ax, int ay)
9787 int element = Feld[ax][ay];
9788 int graphic = el2img(element);
9789 boolean oben_frei = FALSE, unten_frei = FALSE;
9790 boolean links_frei = FALSE, rechts_frei = FALSE;
9791 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9792 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9793 boolean new_wall = FALSE;
9795 if (IS_ANIMATED(graphic))
9796 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9798 if (!MovDelay[ax][ay]) /* start building new wall */
9799 MovDelay[ax][ay] = 6;
9801 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9804 if (MovDelay[ax][ay])
9808 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9810 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9812 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9814 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9817 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9818 element == EL_EXPANDABLE_WALL_ANY)
9822 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9823 Store[ax][ay-1] = element;
9824 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9825 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9826 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9827 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9832 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9833 Store[ax][ay+1] = element;
9834 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9835 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9836 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9837 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9842 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9843 element == EL_EXPANDABLE_WALL_ANY ||
9844 element == EL_EXPANDABLE_WALL ||
9845 element == EL_BD_EXPANDABLE_WALL)
9849 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9850 Store[ax-1][ay] = element;
9851 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9852 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9853 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9854 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9860 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9861 Store[ax+1][ay] = element;
9862 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9863 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9864 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9865 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9870 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9871 TEST_DrawLevelField(ax, ay);
9873 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9875 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9876 unten_massiv = TRUE;
9877 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9878 links_massiv = TRUE;
9879 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9880 rechts_massiv = TRUE;
9882 if (((oben_massiv && unten_massiv) ||
9883 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9884 element == EL_EXPANDABLE_WALL) &&
9885 ((links_massiv && rechts_massiv) ||
9886 element == EL_EXPANDABLE_WALL_VERTICAL))
9887 Feld[ax][ay] = EL_WALL;
9890 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9893 void MauerAblegerStahl(int ax, int ay)
9895 int element = Feld[ax][ay];
9896 int graphic = el2img(element);
9897 boolean oben_frei = FALSE, unten_frei = FALSE;
9898 boolean links_frei = FALSE, rechts_frei = FALSE;
9899 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9900 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9901 boolean new_wall = FALSE;
9903 if (IS_ANIMATED(graphic))
9904 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9906 if (!MovDelay[ax][ay]) /* start building new wall */
9907 MovDelay[ax][ay] = 6;
9909 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9912 if (MovDelay[ax][ay])
9916 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9918 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9920 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9922 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9925 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9926 element == EL_EXPANDABLE_STEELWALL_ANY)
9930 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9931 Store[ax][ay-1] = element;
9932 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9933 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9934 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9935 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9940 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9941 Store[ax][ay+1] = element;
9942 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9943 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9944 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9945 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9950 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9951 element == EL_EXPANDABLE_STEELWALL_ANY)
9955 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9956 Store[ax-1][ay] = element;
9957 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9958 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9959 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9960 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9966 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9967 Store[ax+1][ay] = element;
9968 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9969 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9970 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9971 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9976 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9978 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9979 unten_massiv = TRUE;
9980 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9981 links_massiv = TRUE;
9982 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9983 rechts_massiv = TRUE;
9985 if (((oben_massiv && unten_massiv) ||
9986 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9987 ((links_massiv && rechts_massiv) ||
9988 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9989 Feld[ax][ay] = EL_STEELWALL;
9992 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9995 void CheckForDragon(int x, int y)
9998 boolean dragon_found = FALSE;
9999 static int xy[4][2] =
10007 for (i = 0; i < NUM_DIRECTIONS; i++)
10009 for (j = 0; j < 4; j++)
10011 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10013 if (IN_LEV_FIELD(xx, yy) &&
10014 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10016 if (Feld[xx][yy] == EL_DRAGON)
10017 dragon_found = TRUE;
10026 for (i = 0; i < NUM_DIRECTIONS; i++)
10028 for (j = 0; j < 3; j++)
10030 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10032 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10034 Feld[xx][yy] = EL_EMPTY;
10035 TEST_DrawLevelField(xx, yy);
10044 static void InitBuggyBase(int x, int y)
10046 int element = Feld[x][y];
10047 int activating_delay = FRAMES_PER_SECOND / 4;
10049 ChangeDelay[x][y] =
10050 (element == EL_SP_BUGGY_BASE ?
10051 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10052 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10054 element == EL_SP_BUGGY_BASE_ACTIVE ?
10055 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10058 static void WarnBuggyBase(int x, int y)
10061 static int xy[4][2] =
10069 for (i = 0; i < NUM_DIRECTIONS; i++)
10071 int xx = x + xy[i][0];
10072 int yy = y + xy[i][1];
10074 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10076 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10083 static void InitTrap(int x, int y)
10085 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10088 static void ActivateTrap(int x, int y)
10090 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10093 static void ChangeActiveTrap(int x, int y)
10095 int graphic = IMG_TRAP_ACTIVE;
10097 /* if new animation frame was drawn, correct crumbled sand border */
10098 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10099 TEST_DrawLevelFieldCrumbledSand(x, y);
10102 static int getSpecialActionElement(int element, int number, int base_element)
10104 return (element != EL_EMPTY ? element :
10105 number != -1 ? base_element + number - 1 :
10109 static int getModifiedActionNumber(int value_old, int operator, int operand,
10110 int value_min, int value_max)
10112 int value_new = (operator == CA_MODE_SET ? operand :
10113 operator == CA_MODE_ADD ? value_old + operand :
10114 operator == CA_MODE_SUBTRACT ? value_old - operand :
10115 operator == CA_MODE_MULTIPLY ? value_old * operand :
10116 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10117 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10120 return (value_new < value_min ? value_min :
10121 value_new > value_max ? value_max :
10125 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10127 struct ElementInfo *ei = &element_info[element];
10128 struct ElementChangeInfo *change = &ei->change_page[page];
10129 int target_element = change->target_element;
10130 int action_type = change->action_type;
10131 int action_mode = change->action_mode;
10132 int action_arg = change->action_arg;
10135 if (!change->has_action)
10138 /* ---------- determine action paramater values -------------------------- */
10140 int level_time_value =
10141 (level.time > 0 ? TimeLeft :
10144 int action_arg_element =
10145 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10146 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10147 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10150 int action_arg_direction =
10151 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10152 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10153 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10154 change->actual_trigger_side :
10155 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10156 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10159 int action_arg_number_min =
10160 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10163 int action_arg_number_max =
10164 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10165 action_type == CA_SET_LEVEL_GEMS ? 999 :
10166 action_type == CA_SET_LEVEL_TIME ? 9999 :
10167 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10168 action_type == CA_SET_CE_VALUE ? 9999 :
10169 action_type == CA_SET_CE_SCORE ? 9999 :
10172 int action_arg_number_reset =
10173 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10174 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10175 action_type == CA_SET_LEVEL_TIME ? level.time :
10176 action_type == CA_SET_LEVEL_SCORE ? 0 :
10177 #if USE_NEW_CUSTOM_VALUE
10178 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10180 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10182 action_type == CA_SET_CE_SCORE ? 0 :
10185 int action_arg_number =
10186 (action_arg <= CA_ARG_MAX ? action_arg :
10187 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10188 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10189 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10190 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10191 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10192 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10193 #if USE_NEW_CUSTOM_VALUE
10194 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10196 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10198 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10199 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10200 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10201 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10202 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10203 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10204 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10205 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10206 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10207 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10208 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10211 int action_arg_number_old =
10212 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10213 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10214 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10215 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10216 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10219 int action_arg_number_new =
10220 getModifiedActionNumber(action_arg_number_old,
10221 action_mode, action_arg_number,
10222 action_arg_number_min, action_arg_number_max);
10225 int trigger_player_bits = change->actual_trigger_player_bits;
10227 int trigger_player_bits =
10228 (change->actual_trigger_player >= EL_PLAYER_1 &&
10229 change->actual_trigger_player <= EL_PLAYER_4 ?
10230 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10234 int action_arg_player_bits =
10235 (action_arg >= CA_ARG_PLAYER_1 &&
10236 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10237 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10240 /* ---------- execute action -------------------------------------------- */
10242 switch (action_type)
10249 /* ---------- level actions ------------------------------------------- */
10251 case CA_RESTART_LEVEL:
10253 game.restart_level = TRUE;
10258 case CA_SHOW_ENVELOPE:
10260 int element = getSpecialActionElement(action_arg_element,
10261 action_arg_number, EL_ENVELOPE_1);
10263 if (IS_ENVELOPE(element))
10264 local_player->show_envelope = element;
10269 case CA_SET_LEVEL_TIME:
10271 if (level.time > 0) /* only modify limited time value */
10273 TimeLeft = action_arg_number_new;
10276 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10278 DisplayGameControlValues();
10280 DrawGameValue_Time(TimeLeft);
10283 if (!TimeLeft && setup.time_limit)
10284 for (i = 0; i < MAX_PLAYERS; i++)
10285 KillPlayer(&stored_player[i]);
10291 case CA_SET_LEVEL_SCORE:
10293 local_player->score = action_arg_number_new;
10296 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10298 DisplayGameControlValues();
10300 DrawGameValue_Score(local_player->score);
10306 case CA_SET_LEVEL_GEMS:
10308 local_player->gems_still_needed = action_arg_number_new;
10311 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10313 DisplayGameControlValues();
10315 DrawGameValue_Emeralds(local_player->gems_still_needed);
10321 #if !USE_PLAYER_GRAVITY
10322 case CA_SET_LEVEL_GRAVITY:
10324 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10325 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10326 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10332 case CA_SET_LEVEL_WIND:
10334 game.wind_direction = action_arg_direction;
10339 /* ---------- player actions ------------------------------------------ */
10341 case CA_MOVE_PLAYER:
10343 /* automatically move to the next field in specified direction */
10344 for (i = 0; i < MAX_PLAYERS; i++)
10345 if (trigger_player_bits & (1 << i))
10346 stored_player[i].programmed_action = action_arg_direction;
10351 case CA_EXIT_PLAYER:
10353 for (i = 0; i < MAX_PLAYERS; i++)
10354 if (action_arg_player_bits & (1 << i))
10355 PlayerWins(&stored_player[i]);
10360 case CA_KILL_PLAYER:
10362 for (i = 0; i < MAX_PLAYERS; i++)
10363 if (action_arg_player_bits & (1 << i))
10364 KillPlayer(&stored_player[i]);
10369 case CA_SET_PLAYER_KEYS:
10371 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10372 int element = getSpecialActionElement(action_arg_element,
10373 action_arg_number, EL_KEY_1);
10375 if (IS_KEY(element))
10377 for (i = 0; i < MAX_PLAYERS; i++)
10379 if (trigger_player_bits & (1 << i))
10381 stored_player[i].key[KEY_NR(element)] = key_state;
10383 DrawGameDoorValues();
10391 case CA_SET_PLAYER_SPEED:
10393 for (i = 0; i < MAX_PLAYERS; i++)
10395 if (trigger_player_bits & (1 << i))
10397 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10399 if (action_arg == CA_ARG_SPEED_FASTER &&
10400 stored_player[i].cannot_move)
10402 action_arg_number = STEPSIZE_VERY_SLOW;
10404 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10405 action_arg == CA_ARG_SPEED_FASTER)
10407 action_arg_number = 2;
10408 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10411 else if (action_arg == CA_ARG_NUMBER_RESET)
10413 action_arg_number = level.initial_player_stepsize[i];
10417 getModifiedActionNumber(move_stepsize,
10420 action_arg_number_min,
10421 action_arg_number_max);
10423 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10430 case CA_SET_PLAYER_SHIELD:
10432 for (i = 0; i < MAX_PLAYERS; i++)
10434 if (trigger_player_bits & (1 << i))
10436 if (action_arg == CA_ARG_SHIELD_OFF)
10438 stored_player[i].shield_normal_time_left = 0;
10439 stored_player[i].shield_deadly_time_left = 0;
10441 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10443 stored_player[i].shield_normal_time_left = 999999;
10445 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10447 stored_player[i].shield_normal_time_left = 999999;
10448 stored_player[i].shield_deadly_time_left = 999999;
10456 #if USE_PLAYER_GRAVITY
10457 case CA_SET_PLAYER_GRAVITY:
10459 for (i = 0; i < MAX_PLAYERS; i++)
10461 if (trigger_player_bits & (1 << i))
10463 stored_player[i].gravity =
10464 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10465 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10466 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10467 stored_player[i].gravity);
10475 case CA_SET_PLAYER_ARTWORK:
10477 for (i = 0; i < MAX_PLAYERS; i++)
10479 if (trigger_player_bits & (1 << i))
10481 int artwork_element = action_arg_element;
10483 if (action_arg == CA_ARG_ELEMENT_RESET)
10485 (level.use_artwork_element[i] ? level.artwork_element[i] :
10486 stored_player[i].element_nr);
10488 #if USE_GFX_RESET_PLAYER_ARTWORK
10489 if (stored_player[i].artwork_element != artwork_element)
10490 stored_player[i].Frame = 0;
10493 stored_player[i].artwork_element = artwork_element;
10495 SetPlayerWaiting(&stored_player[i], FALSE);
10497 /* set number of special actions for bored and sleeping animation */
10498 stored_player[i].num_special_action_bored =
10499 get_num_special_action(artwork_element,
10500 ACTION_BORING_1, ACTION_BORING_LAST);
10501 stored_player[i].num_special_action_sleeping =
10502 get_num_special_action(artwork_element,
10503 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10510 /* ---------- CE actions ---------------------------------------------- */
10512 case CA_SET_CE_VALUE:
10514 #if USE_NEW_CUSTOM_VALUE
10515 int last_ce_value = CustomValue[x][y];
10517 CustomValue[x][y] = action_arg_number_new;
10519 if (CustomValue[x][y] != last_ce_value)
10521 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10522 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10524 if (CustomValue[x][y] == 0)
10526 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10527 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10535 case CA_SET_CE_SCORE:
10537 #if USE_NEW_CUSTOM_VALUE
10538 int last_ce_score = ei->collect_score;
10540 ei->collect_score = action_arg_number_new;
10542 if (ei->collect_score != last_ce_score)
10544 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10545 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10547 if (ei->collect_score == 0)
10551 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10552 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10555 This is a very special case that seems to be a mixture between
10556 CheckElementChange() and CheckTriggeredElementChange(): while
10557 the first one only affects single elements that are triggered
10558 directly, the second one affects multiple elements in the playfield
10559 that are triggered indirectly by another element. This is a third
10560 case: Changing the CE score always affects multiple identical CEs,
10561 so every affected CE must be checked, not only the single CE for
10562 which the CE score was changed in the first place (as every instance
10563 of that CE shares the same CE score, and therefore also can change)!
10565 SCAN_PLAYFIELD(xx, yy)
10567 if (Feld[xx][yy] == element)
10568 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10569 CE_SCORE_GETS_ZERO);
10578 /* ---------- engine actions ------------------------------------------ */
10580 case CA_SET_ENGINE_SCAN_MODE:
10582 InitPlayfieldScanMode(action_arg);
10592 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10594 int old_element = Feld[x][y];
10595 int new_element = GetElementFromGroupElement(element);
10596 int previous_move_direction = MovDir[x][y];
10597 #if USE_NEW_CUSTOM_VALUE
10598 int last_ce_value = CustomValue[x][y];
10600 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10601 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10602 boolean add_player_onto_element = (new_element_is_player &&
10603 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10604 /* this breaks SnakeBite when a snake is
10605 halfway through a door that closes */
10606 /* NOW FIXED AT LEVEL INIT IN files.c */
10607 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10609 IS_WALKABLE(old_element));
10612 /* check if element under the player changes from accessible to unaccessible
10613 (needed for special case of dropping element which then changes) */
10614 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10615 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10623 if (!add_player_onto_element)
10625 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10626 RemoveMovingField(x, y);
10630 Feld[x][y] = new_element;
10632 #if !USE_GFX_RESET_GFX_ANIMATION
10633 ResetGfxAnimation(x, y);
10634 ResetRandomAnimationValue(x, y);
10637 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10638 MovDir[x][y] = previous_move_direction;
10640 #if USE_NEW_CUSTOM_VALUE
10641 if (element_info[new_element].use_last_ce_value)
10642 CustomValue[x][y] = last_ce_value;
10645 InitField_WithBug1(x, y, FALSE);
10647 new_element = Feld[x][y]; /* element may have changed */
10649 #if USE_GFX_RESET_GFX_ANIMATION
10650 ResetGfxAnimation(x, y);
10651 ResetRandomAnimationValue(x, y);
10654 TEST_DrawLevelField(x, y);
10656 if (GFX_CRUMBLED(new_element))
10657 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10661 /* check if element under the player changes from accessible to unaccessible
10662 (needed for special case of dropping element which then changes) */
10663 /* (must be checked after creating new element for walkable group elements) */
10664 #if USE_FIX_KILLED_BY_NON_WALKABLE
10665 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10666 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10673 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10674 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10683 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10684 if (new_element_is_player)
10685 RelocatePlayer(x, y, new_element);
10688 ChangeCount[x][y]++; /* count number of changes in the same frame */
10690 TestIfBadThingTouchesPlayer(x, y);
10691 TestIfPlayerTouchesCustomElement(x, y);
10692 TestIfElementTouchesCustomElement(x, y);
10695 static void CreateField(int x, int y, int element)
10697 CreateFieldExt(x, y, element, FALSE);
10700 static void CreateElementFromChange(int x, int y, int element)
10702 element = GET_VALID_RUNTIME_ELEMENT(element);
10704 #if USE_STOP_CHANGED_ELEMENTS
10705 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10707 int old_element = Feld[x][y];
10709 /* prevent changed element from moving in same engine frame
10710 unless both old and new element can either fall or move */
10711 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10712 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10717 CreateFieldExt(x, y, element, TRUE);
10720 static boolean ChangeElement(int x, int y, int element, int page)
10722 struct ElementInfo *ei = &element_info[element];
10723 struct ElementChangeInfo *change = &ei->change_page[page];
10724 int ce_value = CustomValue[x][y];
10725 int ce_score = ei->collect_score;
10726 int target_element;
10727 int old_element = Feld[x][y];
10729 /* always use default change event to prevent running into a loop */
10730 if (ChangeEvent[x][y] == -1)
10731 ChangeEvent[x][y] = CE_DELAY;
10733 if (ChangeEvent[x][y] == CE_DELAY)
10735 /* reset actual trigger element, trigger player and action element */
10736 change->actual_trigger_element = EL_EMPTY;
10737 change->actual_trigger_player = EL_PLAYER_1;
10738 change->actual_trigger_player_bits = CH_PLAYER_1;
10739 change->actual_trigger_side = CH_SIDE_NONE;
10740 change->actual_trigger_ce_value = 0;
10741 change->actual_trigger_ce_score = 0;
10744 /* do not change elements more than a specified maximum number of changes */
10745 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10748 ChangeCount[x][y]++; /* count number of changes in the same frame */
10750 if (change->explode)
10757 if (change->use_target_content)
10759 boolean complete_replace = TRUE;
10760 boolean can_replace[3][3];
10763 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10766 boolean is_walkable;
10767 boolean is_diggable;
10768 boolean is_collectible;
10769 boolean is_removable;
10770 boolean is_destructible;
10771 int ex = x + xx - 1;
10772 int ey = y + yy - 1;
10773 int content_element = change->target_content.e[xx][yy];
10776 can_replace[xx][yy] = TRUE;
10778 if (ex == x && ey == y) /* do not check changing element itself */
10781 if (content_element == EL_EMPTY_SPACE)
10783 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10788 if (!IN_LEV_FIELD(ex, ey))
10790 can_replace[xx][yy] = FALSE;
10791 complete_replace = FALSE;
10798 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10799 e = MovingOrBlocked2Element(ex, ey);
10801 is_empty = (IS_FREE(ex, ey) ||
10802 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10804 is_walkable = (is_empty || IS_WALKABLE(e));
10805 is_diggable = (is_empty || IS_DIGGABLE(e));
10806 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10807 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10808 is_removable = (is_diggable || is_collectible);
10810 can_replace[xx][yy] =
10811 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10812 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10813 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10814 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10815 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10816 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10817 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10819 if (!can_replace[xx][yy])
10820 complete_replace = FALSE;
10823 if (!change->only_if_complete || complete_replace)
10825 boolean something_has_changed = FALSE;
10827 if (change->only_if_complete && change->use_random_replace &&
10828 RND(100) < change->random_percentage)
10831 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10833 int ex = x + xx - 1;
10834 int ey = y + yy - 1;
10835 int content_element;
10837 if (can_replace[xx][yy] && (!change->use_random_replace ||
10838 RND(100) < change->random_percentage))
10840 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10841 RemoveMovingField(ex, ey);
10843 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10845 content_element = change->target_content.e[xx][yy];
10846 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10847 ce_value, ce_score);
10849 CreateElementFromChange(ex, ey, target_element);
10851 something_has_changed = TRUE;
10853 /* for symmetry reasons, freeze newly created border elements */
10854 if (ex != x || ey != y)
10855 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10859 if (something_has_changed)
10861 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10862 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10868 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10869 ce_value, ce_score);
10871 if (element == EL_DIAGONAL_GROWING ||
10872 element == EL_DIAGONAL_SHRINKING)
10874 target_element = Store[x][y];
10876 Store[x][y] = EL_EMPTY;
10879 CreateElementFromChange(x, y, target_element);
10881 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10882 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10885 /* this uses direct change before indirect change */
10886 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10891 #if USE_NEW_DELAYED_ACTION
10893 static void HandleElementChange(int x, int y, int page)
10895 int element = MovingOrBlocked2Element(x, y);
10896 struct ElementInfo *ei = &element_info[element];
10897 struct ElementChangeInfo *change = &ei->change_page[page];
10900 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10901 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10904 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10905 x, y, element, element_info[element].token_name);
10906 printf("HandleElementChange(): This should never happen!\n");
10911 /* this can happen with classic bombs on walkable, changing elements */
10912 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10915 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10916 ChangeDelay[x][y] = 0;
10922 if (ChangeDelay[x][y] == 0) /* initialize element change */
10924 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10926 if (change->can_change)
10929 /* !!! not clear why graphic animation should be reset at all here !!! */
10930 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10931 #if USE_GFX_RESET_WHEN_NOT_MOVING
10932 /* when a custom element is about to change (for example by change delay),
10933 do not reset graphic animation when the custom element is moving */
10934 if (!IS_MOVING(x, y))
10937 ResetGfxAnimation(x, y);
10938 ResetRandomAnimationValue(x, y);
10942 if (change->pre_change_function)
10943 change->pre_change_function(x, y);
10947 ChangeDelay[x][y]--;
10949 if (ChangeDelay[x][y] != 0) /* continue element change */
10951 if (change->can_change)
10953 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10955 if (IS_ANIMATED(graphic))
10956 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10958 if (change->change_function)
10959 change->change_function(x, y);
10962 else /* finish element change */
10964 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10966 page = ChangePage[x][y];
10967 ChangePage[x][y] = -1;
10969 change = &ei->change_page[page];
10972 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10974 ChangeDelay[x][y] = 1; /* try change after next move step */
10975 ChangePage[x][y] = page; /* remember page to use for change */
10980 if (change->can_change)
10982 if (ChangeElement(x, y, element, page))
10984 if (change->post_change_function)
10985 change->post_change_function(x, y);
10989 if (change->has_action)
10990 ExecuteCustomElementAction(x, y, element, page);
10996 static void HandleElementChange(int x, int y, int page)
10998 int element = MovingOrBlocked2Element(x, y);
10999 struct ElementInfo *ei = &element_info[element];
11000 struct ElementChangeInfo *change = &ei->change_page[page];
11003 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11006 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11007 x, y, element, element_info[element].token_name);
11008 printf("HandleElementChange(): This should never happen!\n");
11013 /* this can happen with classic bombs on walkable, changing elements */
11014 if (!CAN_CHANGE(element))
11017 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11018 ChangeDelay[x][y] = 0;
11024 if (ChangeDelay[x][y] == 0) /* initialize element change */
11026 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11028 ResetGfxAnimation(x, y);
11029 ResetRandomAnimationValue(x, y);
11031 if (change->pre_change_function)
11032 change->pre_change_function(x, y);
11035 ChangeDelay[x][y]--;
11037 if (ChangeDelay[x][y] != 0) /* continue element change */
11039 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11041 if (IS_ANIMATED(graphic))
11042 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11044 if (change->change_function)
11045 change->change_function(x, y);
11047 else /* finish element change */
11049 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11051 page = ChangePage[x][y];
11052 ChangePage[x][y] = -1;
11054 change = &ei->change_page[page];
11057 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11059 ChangeDelay[x][y] = 1; /* try change after next move step */
11060 ChangePage[x][y] = page; /* remember page to use for change */
11065 if (ChangeElement(x, y, element, page))
11067 if (change->post_change_function)
11068 change->post_change_function(x, y);
11075 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11076 int trigger_element,
11078 int trigger_player,
11082 boolean change_done_any = FALSE;
11083 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11086 if (!(trigger_events[trigger_element][trigger_event]))
11090 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11091 trigger_event, recursion_loop_depth, recursion_loop_detected,
11092 recursion_loop_element, EL_NAME(recursion_loop_element));
11095 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11097 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11099 int element = EL_CUSTOM_START + i;
11100 boolean change_done = FALSE;
11103 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11104 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11107 for (p = 0; p < element_info[element].num_change_pages; p++)
11109 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11111 if (change->can_change_or_has_action &&
11112 change->has_event[trigger_event] &&
11113 change->trigger_side & trigger_side &&
11114 change->trigger_player & trigger_player &&
11115 change->trigger_page & trigger_page_bits &&
11116 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11118 change->actual_trigger_element = trigger_element;
11119 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11120 change->actual_trigger_player_bits = trigger_player;
11121 change->actual_trigger_side = trigger_side;
11122 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11123 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11125 if ((change->can_change && !change_done) || change->has_action)
11129 SCAN_PLAYFIELD(x, y)
11131 if (Feld[x][y] == element)
11133 if (change->can_change && !change_done)
11135 ChangeDelay[x][y] = 1;
11136 ChangeEvent[x][y] = trigger_event;
11138 HandleElementChange(x, y, p);
11140 #if USE_NEW_DELAYED_ACTION
11141 else if (change->has_action)
11143 ExecuteCustomElementAction(x, y, element, p);
11144 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11147 if (change->has_action)
11149 ExecuteCustomElementAction(x, y, element, p);
11150 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11156 if (change->can_change)
11158 change_done = TRUE;
11159 change_done_any = TRUE;
11166 RECURSION_LOOP_DETECTION_END();
11168 return change_done_any;
11171 static boolean CheckElementChangeExt(int x, int y,
11173 int trigger_element,
11175 int trigger_player,
11178 boolean change_done = FALSE;
11181 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11182 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11185 if (Feld[x][y] == EL_BLOCKED)
11187 Blocked2Moving(x, y, &x, &y);
11188 element = Feld[x][y];
11192 /* check if element has already changed */
11193 if (Feld[x][y] != element)
11196 /* check if element has already changed or is about to change after moving */
11197 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11198 Feld[x][y] != element) ||
11200 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11201 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11202 ChangePage[x][y] != -1)))
11207 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11208 trigger_event, recursion_loop_depth, recursion_loop_detected,
11209 recursion_loop_element, EL_NAME(recursion_loop_element));
11212 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11214 for (p = 0; p < element_info[element].num_change_pages; p++)
11216 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11218 /* check trigger element for all events where the element that is checked
11219 for changing interacts with a directly adjacent element -- this is
11220 different to element changes that affect other elements to change on the
11221 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11222 boolean check_trigger_element =
11223 (trigger_event == CE_TOUCHING_X ||
11224 trigger_event == CE_HITTING_X ||
11225 trigger_event == CE_HIT_BY_X ||
11227 /* this one was forgotten until 3.2.3 */
11228 trigger_event == CE_DIGGING_X);
11231 if (change->can_change_or_has_action &&
11232 change->has_event[trigger_event] &&
11233 change->trigger_side & trigger_side &&
11234 change->trigger_player & trigger_player &&
11235 (!check_trigger_element ||
11236 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11238 change->actual_trigger_element = trigger_element;
11239 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11240 change->actual_trigger_player_bits = trigger_player;
11241 change->actual_trigger_side = trigger_side;
11242 change->actual_trigger_ce_value = CustomValue[x][y];
11243 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11245 /* special case: trigger element not at (x,y) position for some events */
11246 if (check_trigger_element)
11258 { 0, 0 }, { 0, 0 }, { 0, 0 },
11262 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11263 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11265 change->actual_trigger_ce_value = CustomValue[xx][yy];
11266 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11269 if (change->can_change && !change_done)
11271 ChangeDelay[x][y] = 1;
11272 ChangeEvent[x][y] = trigger_event;
11274 HandleElementChange(x, y, p);
11276 change_done = TRUE;
11278 #if USE_NEW_DELAYED_ACTION
11279 else if (change->has_action)
11281 ExecuteCustomElementAction(x, y, element, p);
11282 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11285 if (change->has_action)
11287 ExecuteCustomElementAction(x, y, element, p);
11288 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11294 RECURSION_LOOP_DETECTION_END();
11296 return change_done;
11299 static void PlayPlayerSound(struct PlayerInfo *player)
11301 int jx = player->jx, jy = player->jy;
11302 int sound_element = player->artwork_element;
11303 int last_action = player->last_action_waiting;
11304 int action = player->action_waiting;
11306 if (player->is_waiting)
11308 if (action != last_action)
11309 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11311 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11315 if (action != last_action)
11316 StopSound(element_info[sound_element].sound[last_action]);
11318 if (last_action == ACTION_SLEEPING)
11319 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11323 static void PlayAllPlayersSound()
11327 for (i = 0; i < MAX_PLAYERS; i++)
11328 if (stored_player[i].active)
11329 PlayPlayerSound(&stored_player[i]);
11332 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11334 boolean last_waiting = player->is_waiting;
11335 int move_dir = player->MovDir;
11337 player->dir_waiting = move_dir;
11338 player->last_action_waiting = player->action_waiting;
11342 if (!last_waiting) /* not waiting -> waiting */
11344 player->is_waiting = TRUE;
11346 player->frame_counter_bored =
11348 game.player_boring_delay_fixed +
11349 GetSimpleRandom(game.player_boring_delay_random);
11350 player->frame_counter_sleeping =
11352 game.player_sleeping_delay_fixed +
11353 GetSimpleRandom(game.player_sleeping_delay_random);
11355 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11358 if (game.player_sleeping_delay_fixed +
11359 game.player_sleeping_delay_random > 0 &&
11360 player->anim_delay_counter == 0 &&
11361 player->post_delay_counter == 0 &&
11362 FrameCounter >= player->frame_counter_sleeping)
11363 player->is_sleeping = TRUE;
11364 else if (game.player_boring_delay_fixed +
11365 game.player_boring_delay_random > 0 &&
11366 FrameCounter >= player->frame_counter_bored)
11367 player->is_bored = TRUE;
11369 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11370 player->is_bored ? ACTION_BORING :
11373 if (player->is_sleeping && player->use_murphy)
11375 /* special case for sleeping Murphy when leaning against non-free tile */
11377 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11378 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11379 !IS_MOVING(player->jx - 1, player->jy)))
11380 move_dir = MV_LEFT;
11381 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11382 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11383 !IS_MOVING(player->jx + 1, player->jy)))
11384 move_dir = MV_RIGHT;
11386 player->is_sleeping = FALSE;
11388 player->dir_waiting = move_dir;
11391 if (player->is_sleeping)
11393 if (player->num_special_action_sleeping > 0)
11395 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11397 int last_special_action = player->special_action_sleeping;
11398 int num_special_action = player->num_special_action_sleeping;
11399 int special_action =
11400 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11401 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11402 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11403 last_special_action + 1 : ACTION_SLEEPING);
11404 int special_graphic =
11405 el_act_dir2img(player->artwork_element, special_action, move_dir);
11407 player->anim_delay_counter =
11408 graphic_info[special_graphic].anim_delay_fixed +
11409 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11410 player->post_delay_counter =
11411 graphic_info[special_graphic].post_delay_fixed +
11412 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11414 player->special_action_sleeping = special_action;
11417 if (player->anim_delay_counter > 0)
11419 player->action_waiting = player->special_action_sleeping;
11420 player->anim_delay_counter--;
11422 else if (player->post_delay_counter > 0)
11424 player->post_delay_counter--;
11428 else if (player->is_bored)
11430 if (player->num_special_action_bored > 0)
11432 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11434 int special_action =
11435 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11436 int special_graphic =
11437 el_act_dir2img(player->artwork_element, special_action, move_dir);
11439 player->anim_delay_counter =
11440 graphic_info[special_graphic].anim_delay_fixed +
11441 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11442 player->post_delay_counter =
11443 graphic_info[special_graphic].post_delay_fixed +
11444 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11446 player->special_action_bored = special_action;
11449 if (player->anim_delay_counter > 0)
11451 player->action_waiting = player->special_action_bored;
11452 player->anim_delay_counter--;
11454 else if (player->post_delay_counter > 0)
11456 player->post_delay_counter--;
11461 else if (last_waiting) /* waiting -> not waiting */
11463 player->is_waiting = FALSE;
11464 player->is_bored = FALSE;
11465 player->is_sleeping = FALSE;
11467 player->frame_counter_bored = -1;
11468 player->frame_counter_sleeping = -1;
11470 player->anim_delay_counter = 0;
11471 player->post_delay_counter = 0;
11473 player->dir_waiting = player->MovDir;
11474 player->action_waiting = ACTION_DEFAULT;
11476 player->special_action_bored = ACTION_DEFAULT;
11477 player->special_action_sleeping = ACTION_DEFAULT;
11481 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11483 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11484 int left = player_action & JOY_LEFT;
11485 int right = player_action & JOY_RIGHT;
11486 int up = player_action & JOY_UP;
11487 int down = player_action & JOY_DOWN;
11488 int button1 = player_action & JOY_BUTTON_1;
11489 int button2 = player_action & JOY_BUTTON_2;
11490 int dx = (left ? -1 : right ? 1 : 0);
11491 int dy = (up ? -1 : down ? 1 : 0);
11493 if (!player->active || tape.pausing)
11499 snapped = SnapField(player, dx, dy);
11503 dropped = DropElement(player);
11505 moved = MovePlayer(player, dx, dy);
11508 if (tape.single_step && tape.recording && !tape.pausing)
11510 if (button1 || (dropped && !moved))
11512 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11513 SnapField(player, 0, 0); /* stop snapping */
11517 SetPlayerWaiting(player, FALSE);
11519 return player_action;
11523 /* no actions for this player (no input at player's configured device) */
11525 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11526 SnapField(player, 0, 0);
11527 CheckGravityMovementWhenNotMoving(player);
11529 if (player->MovPos == 0)
11530 SetPlayerWaiting(player, TRUE);
11532 if (player->MovPos == 0) /* needed for tape.playing */
11533 player->is_moving = FALSE;
11535 player->is_dropping = FALSE;
11536 player->is_dropping_pressed = FALSE;
11537 player->drop_pressed_delay = 0;
11543 static void CheckLevelTime()
11547 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11549 if (level.native_em_level->lev->home == 0) /* all players at home */
11551 PlayerWins(local_player);
11553 AllPlayersGone = TRUE;
11555 level.native_em_level->lev->home = -1;
11558 if (level.native_em_level->ply[0]->alive == 0 &&
11559 level.native_em_level->ply[1]->alive == 0 &&
11560 level.native_em_level->ply[2]->alive == 0 &&
11561 level.native_em_level->ply[3]->alive == 0) /* all dead */
11562 AllPlayersGone = TRUE;
11565 if (TimeFrames >= FRAMES_PER_SECOND)
11570 for (i = 0; i < MAX_PLAYERS; i++)
11572 struct PlayerInfo *player = &stored_player[i];
11574 if (SHIELD_ON(player))
11576 player->shield_normal_time_left--;
11578 if (player->shield_deadly_time_left > 0)
11579 player->shield_deadly_time_left--;
11583 if (!local_player->LevelSolved && !level.use_step_counter)
11591 if (TimeLeft <= 10 && setup.time_limit)
11592 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11595 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11597 DisplayGameControlValues();
11599 DrawGameValue_Time(TimeLeft);
11602 if (!TimeLeft && setup.time_limit)
11604 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11605 level.native_em_level->lev->killed_out_of_time = TRUE;
11607 for (i = 0; i < MAX_PLAYERS; i++)
11608 KillPlayer(&stored_player[i]);
11612 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11614 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11616 DisplayGameControlValues();
11619 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11620 DrawGameValue_Time(TimePlayed);
11623 level.native_em_level->lev->time =
11624 (level.time == 0 ? TimePlayed : TimeLeft);
11627 if (tape.recording || tape.playing)
11628 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11632 UpdateAndDisplayGameControlValues();
11634 UpdateGameDoorValues();
11635 DrawGameDoorValues();
11639 void AdvanceFrameAndPlayerCounters(int player_nr)
11643 /* advance frame counters (global frame counter and time frame counter) */
11647 /* advance player counters (counters for move delay, move animation etc.) */
11648 for (i = 0; i < MAX_PLAYERS; i++)
11650 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11651 int move_delay_value = stored_player[i].move_delay_value;
11652 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11654 if (!advance_player_counters) /* not all players may be affected */
11657 #if USE_NEW_PLAYER_ANIM
11658 if (move_frames == 0) /* less than one move per game frame */
11660 int stepsize = TILEX / move_delay_value;
11661 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11662 int count = (stored_player[i].is_moving ?
11663 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11665 if (count % delay == 0)
11670 stored_player[i].Frame += move_frames;
11672 if (stored_player[i].MovPos != 0)
11673 stored_player[i].StepFrame += move_frames;
11675 if (stored_player[i].move_delay > 0)
11676 stored_player[i].move_delay--;
11678 /* due to bugs in previous versions, counter must count up, not down */
11679 if (stored_player[i].push_delay != -1)
11680 stored_player[i].push_delay++;
11682 if (stored_player[i].drop_delay > 0)
11683 stored_player[i].drop_delay--;
11685 if (stored_player[i].is_dropping_pressed)
11686 stored_player[i].drop_pressed_delay++;
11690 void StartGameActions(boolean init_network_game, boolean record_tape,
11693 unsigned long new_random_seed = InitRND(random_seed);
11696 TapeStartRecording(new_random_seed);
11698 #if defined(NETWORK_AVALIABLE)
11699 if (init_network_game)
11701 SendToServer_StartPlaying();
11712 static unsigned long game_frame_delay = 0;
11713 unsigned long game_frame_delay_value;
11714 byte *recorded_player_action;
11715 byte summarized_player_action = 0;
11716 byte tape_action[MAX_PLAYERS];
11719 /* detect endless loops, caused by custom element programming */
11720 if (recursion_loop_detected && recursion_loop_depth == 0)
11722 char *message = getStringCat3("Internal Error ! Element ",
11723 EL_NAME(recursion_loop_element),
11724 " caused endless loop ! Quit the game ?");
11726 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11727 EL_NAME(recursion_loop_element));
11729 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11731 recursion_loop_detected = FALSE; /* if game should be continued */
11738 if (game.restart_level)
11739 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11741 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11743 if (level.native_em_level->lev->home == 0) /* all players at home */
11745 PlayerWins(local_player);
11747 AllPlayersGone = TRUE;
11749 level.native_em_level->lev->home = -1;
11752 if (level.native_em_level->ply[0]->alive == 0 &&
11753 level.native_em_level->ply[1]->alive == 0 &&
11754 level.native_em_level->ply[2]->alive == 0 &&
11755 level.native_em_level->ply[3]->alive == 0) /* all dead */
11756 AllPlayersGone = TRUE;
11759 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11762 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11765 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11768 game_frame_delay_value =
11769 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11771 if (tape.playing && tape.warp_forward && !tape.pausing)
11772 game_frame_delay_value = 0;
11774 /* ---------- main game synchronization point ---------- */
11776 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11778 if (network_playing && !network_player_action_received)
11780 /* try to get network player actions in time */
11782 #if defined(NETWORK_AVALIABLE)
11783 /* last chance to get network player actions without main loop delay */
11784 HandleNetworking();
11787 /* game was quit by network peer */
11788 if (game_status != GAME_MODE_PLAYING)
11791 if (!network_player_action_received)
11792 return; /* failed to get network player actions in time */
11794 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11800 /* at this point we know that we really continue executing the game */
11802 network_player_action_received = FALSE;
11804 /* when playing tape, read previously recorded player input from tape data */
11805 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11808 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11813 if (tape.set_centered_player)
11815 game.centered_player_nr_next = tape.centered_player_nr_next;
11816 game.set_centered_player = TRUE;
11819 for (i = 0; i < MAX_PLAYERS; i++)
11821 summarized_player_action |= stored_player[i].action;
11823 if (!network_playing)
11824 stored_player[i].effective_action = stored_player[i].action;
11827 #if defined(NETWORK_AVALIABLE)
11828 if (network_playing)
11829 SendToServer_MovePlayer(summarized_player_action);
11832 if (!options.network && !setup.team_mode)
11833 local_player->effective_action = summarized_player_action;
11835 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11837 for (i = 0; i < MAX_PLAYERS; i++)
11838 stored_player[i].effective_action =
11839 (i == game.centered_player_nr ? summarized_player_action : 0);
11842 if (recorded_player_action != NULL)
11843 for (i = 0; i < MAX_PLAYERS; i++)
11844 stored_player[i].effective_action = recorded_player_action[i];
11846 for (i = 0; i < MAX_PLAYERS; i++)
11848 tape_action[i] = stored_player[i].effective_action;
11850 /* (this can only happen in the R'n'D game engine) */
11851 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11852 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11855 /* only record actions from input devices, but not programmed actions */
11856 if (tape.recording)
11857 TapeRecordAction(tape_action);
11859 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11861 GameActions_EM_Main();
11869 void GameActions_EM_Main()
11871 byte effective_action[MAX_PLAYERS];
11872 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11875 for (i = 0; i < MAX_PLAYERS; i++)
11876 effective_action[i] = stored_player[i].effective_action;
11878 GameActions_EM(effective_action, warp_mode);
11882 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11885 void GameActions_RND()
11887 int magic_wall_x = 0, magic_wall_y = 0;
11888 int i, x, y, element, graphic;
11890 InitPlayfieldScanModeVars();
11892 #if USE_ONE_MORE_CHANGE_PER_FRAME
11893 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11895 SCAN_PLAYFIELD(x, y)
11897 ChangeCount[x][y] = 0;
11898 ChangeEvent[x][y] = -1;
11903 if (game.set_centered_player)
11905 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11907 /* switching to "all players" only possible if all players fit to screen */
11908 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11910 game.centered_player_nr_next = game.centered_player_nr;
11911 game.set_centered_player = FALSE;
11914 /* do not switch focus to non-existing (or non-active) player */
11915 if (game.centered_player_nr_next >= 0 &&
11916 !stored_player[game.centered_player_nr_next].active)
11918 game.centered_player_nr_next = game.centered_player_nr;
11919 game.set_centered_player = FALSE;
11923 if (game.set_centered_player &&
11924 ScreenMovPos == 0) /* screen currently aligned at tile position */
11928 if (game.centered_player_nr_next == -1)
11930 setScreenCenteredToAllPlayers(&sx, &sy);
11934 sx = stored_player[game.centered_player_nr_next].jx;
11935 sy = stored_player[game.centered_player_nr_next].jy;
11938 game.centered_player_nr = game.centered_player_nr_next;
11939 game.set_centered_player = FALSE;
11941 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11942 DrawGameDoorValues();
11945 for (i = 0; i < MAX_PLAYERS; i++)
11947 int actual_player_action = stored_player[i].effective_action;
11950 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11951 - rnd_equinox_tetrachloride 048
11952 - rnd_equinox_tetrachloride_ii 096
11953 - rnd_emanuel_schmieg 002
11954 - doctor_sloan_ww 001, 020
11956 if (stored_player[i].MovPos == 0)
11957 CheckGravityMovement(&stored_player[i]);
11960 /* overwrite programmed action with tape action */
11961 if (stored_player[i].programmed_action)
11962 actual_player_action = stored_player[i].programmed_action;
11964 PlayerActions(&stored_player[i], actual_player_action);
11966 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11969 ScrollScreen(NULL, SCROLL_GO_ON);
11971 /* for backwards compatibility, the following code emulates a fixed bug that
11972 occured when pushing elements (causing elements that just made their last
11973 pushing step to already (if possible) make their first falling step in the
11974 same game frame, which is bad); this code is also needed to use the famous
11975 "spring push bug" which is used in older levels and might be wanted to be
11976 used also in newer levels, but in this case the buggy pushing code is only
11977 affecting the "spring" element and no other elements */
11979 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11981 for (i = 0; i < MAX_PLAYERS; i++)
11983 struct PlayerInfo *player = &stored_player[i];
11984 int x = player->jx;
11985 int y = player->jy;
11987 if (player->active && player->is_pushing && player->is_moving &&
11989 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11990 Feld[x][y] == EL_SPRING))
11992 ContinueMoving(x, y);
11994 /* continue moving after pushing (this is actually a bug) */
11995 if (!IS_MOVING(x, y))
11996 Stop[x][y] = FALSE;
12002 debug_print_timestamp(0, "start main loop profiling");
12005 SCAN_PLAYFIELD(x, y)
12007 ChangeCount[x][y] = 0;
12008 ChangeEvent[x][y] = -1;
12010 /* this must be handled before main playfield loop */
12011 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12014 if (MovDelay[x][y] <= 0)
12018 #if USE_NEW_SNAP_DELAY
12019 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12022 if (MovDelay[x][y] <= 0)
12025 TEST_DrawLevelField(x, y);
12027 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12033 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12035 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12036 printf("GameActions(): This should never happen!\n");
12038 ChangePage[x][y] = -1;
12042 Stop[x][y] = FALSE;
12043 if (WasJustMoving[x][y] > 0)
12044 WasJustMoving[x][y]--;
12045 if (WasJustFalling[x][y] > 0)
12046 WasJustFalling[x][y]--;
12047 if (CheckCollision[x][y] > 0)
12048 CheckCollision[x][y]--;
12049 if (CheckImpact[x][y] > 0)
12050 CheckImpact[x][y]--;
12054 /* reset finished pushing action (not done in ContinueMoving() to allow
12055 continuous pushing animation for elements with zero push delay) */
12056 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12058 ResetGfxAnimation(x, y);
12059 TEST_DrawLevelField(x, y);
12063 if (IS_BLOCKED(x, y))
12067 Blocked2Moving(x, y, &oldx, &oldy);
12068 if (!IS_MOVING(oldx, oldy))
12070 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12071 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12072 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12073 printf("GameActions(): This should never happen!\n");
12080 debug_print_timestamp(0, "- time for pre-main loop:");
12083 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12084 SCAN_PLAYFIELD(x, y)
12086 element = Feld[x][y];
12087 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12092 int element2 = element;
12093 int graphic2 = graphic;
12095 int element2 = Feld[x][y];
12096 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12098 int last_gfx_frame = GfxFrame[x][y];
12100 if (graphic_info[graphic2].anim_global_sync)
12101 GfxFrame[x][y] = FrameCounter;
12102 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12103 GfxFrame[x][y] = CustomValue[x][y];
12104 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12105 GfxFrame[x][y] = element_info[element2].collect_score;
12106 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12107 GfxFrame[x][y] = ChangeDelay[x][y];
12109 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12110 DrawLevelGraphicAnimation(x, y, graphic2);
12113 ResetGfxFrame(x, y, TRUE);
12117 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12118 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12119 ResetRandomAnimationValue(x, y);
12123 SetRandomAnimationValue(x, y);
12127 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12130 #endif // -------------------- !!! TEST ONLY !!! --------------------
12133 debug_print_timestamp(0, "- time for TEST loop: -->");
12136 SCAN_PLAYFIELD(x, y)
12138 element = Feld[x][y];
12139 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12141 ResetGfxFrame(x, y, TRUE);
12143 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12144 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12145 ResetRandomAnimationValue(x, y);
12147 SetRandomAnimationValue(x, y);
12149 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12151 if (IS_INACTIVE(element))
12153 if (IS_ANIMATED(graphic))
12154 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12159 /* this may take place after moving, so 'element' may have changed */
12160 if (IS_CHANGING(x, y) &&
12161 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12163 int page = element_info[element].event_page_nr[CE_DELAY];
12166 HandleElementChange(x, y, page);
12168 if (CAN_CHANGE(element))
12169 HandleElementChange(x, y, page);
12171 if (HAS_ACTION(element))
12172 ExecuteCustomElementAction(x, y, element, page);
12175 element = Feld[x][y];
12176 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12179 #if 0 // ---------------------------------------------------------------------
12181 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12185 element = Feld[x][y];
12186 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12188 if (IS_ANIMATED(graphic) &&
12189 !IS_MOVING(x, y) &&
12191 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12193 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12194 TEST_DrawTwinkleOnField(x, y);
12196 else if (IS_MOVING(x, y))
12197 ContinueMoving(x, y);
12204 case EL_EM_EXIT_OPEN:
12205 case EL_SP_EXIT_OPEN:
12206 case EL_STEEL_EXIT_OPEN:
12207 case EL_EM_STEEL_EXIT_OPEN:
12208 case EL_SP_TERMINAL:
12209 case EL_SP_TERMINAL_ACTIVE:
12210 case EL_EXTRA_TIME:
12211 case EL_SHIELD_NORMAL:
12212 case EL_SHIELD_DEADLY:
12213 if (IS_ANIMATED(graphic))
12214 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12217 case EL_DYNAMITE_ACTIVE:
12218 case EL_EM_DYNAMITE_ACTIVE:
12219 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12220 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12221 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12222 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12223 case EL_SP_DISK_RED_ACTIVE:
12224 CheckDynamite(x, y);
12227 case EL_AMOEBA_GROWING:
12228 AmoebeWaechst(x, y);
12231 case EL_AMOEBA_SHRINKING:
12232 AmoebaDisappearing(x, y);
12235 #if !USE_NEW_AMOEBA_CODE
12236 case EL_AMOEBA_WET:
12237 case EL_AMOEBA_DRY:
12238 case EL_AMOEBA_FULL:
12240 case EL_EMC_DRIPPER:
12241 AmoebeAbleger(x, y);
12245 case EL_GAME_OF_LIFE:
12250 case EL_EXIT_CLOSED:
12254 case EL_EM_EXIT_CLOSED:
12258 case EL_STEEL_EXIT_CLOSED:
12259 CheckExitSteel(x, y);
12262 case EL_EM_STEEL_EXIT_CLOSED:
12263 CheckExitSteelEM(x, y);
12266 case EL_SP_EXIT_CLOSED:
12270 case EL_EXPANDABLE_WALL_GROWING:
12271 case EL_EXPANDABLE_STEELWALL_GROWING:
12272 MauerWaechst(x, y);
12275 case EL_EXPANDABLE_WALL:
12276 case EL_EXPANDABLE_WALL_HORIZONTAL:
12277 case EL_EXPANDABLE_WALL_VERTICAL:
12278 case EL_EXPANDABLE_WALL_ANY:
12279 case EL_BD_EXPANDABLE_WALL:
12280 MauerAbleger(x, y);
12283 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12284 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12285 case EL_EXPANDABLE_STEELWALL_ANY:
12286 MauerAblegerStahl(x, y);
12290 CheckForDragon(x, y);
12296 case EL_ELEMENT_SNAPPING:
12297 case EL_DIAGONAL_SHRINKING:
12298 case EL_DIAGONAL_GROWING:
12301 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12303 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12308 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12309 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12314 #else // ---------------------------------------------------------------------
12316 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12320 element = Feld[x][y];
12321 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12323 if (IS_ANIMATED(graphic) &&
12324 !IS_MOVING(x, y) &&
12326 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12328 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12329 TEST_DrawTwinkleOnField(x, y);
12331 else if ((element == EL_ACID ||
12332 element == EL_EXIT_OPEN ||
12333 element == EL_EM_EXIT_OPEN ||
12334 element == EL_SP_EXIT_OPEN ||
12335 element == EL_STEEL_EXIT_OPEN ||
12336 element == EL_EM_STEEL_EXIT_OPEN ||
12337 element == EL_SP_TERMINAL ||
12338 element == EL_SP_TERMINAL_ACTIVE ||
12339 element == EL_EXTRA_TIME ||
12340 element == EL_SHIELD_NORMAL ||
12341 element == EL_SHIELD_DEADLY) &&
12342 IS_ANIMATED(graphic))
12343 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12344 else if (IS_MOVING(x, y))
12345 ContinueMoving(x, y);
12346 else if (IS_ACTIVE_BOMB(element))
12347 CheckDynamite(x, y);
12348 else if (element == EL_AMOEBA_GROWING)
12349 AmoebeWaechst(x, y);
12350 else if (element == EL_AMOEBA_SHRINKING)
12351 AmoebaDisappearing(x, y);
12353 #if !USE_NEW_AMOEBA_CODE
12354 else if (IS_AMOEBALIVE(element))
12355 AmoebeAbleger(x, y);
12358 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12360 else if (element == EL_EXIT_CLOSED)
12362 else if (element == EL_EM_EXIT_CLOSED)
12364 else if (element == EL_STEEL_EXIT_CLOSED)
12365 CheckExitSteel(x, y);
12366 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12367 CheckExitSteelEM(x, y);
12368 else if (element == EL_SP_EXIT_CLOSED)
12370 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12371 element == EL_EXPANDABLE_STEELWALL_GROWING)
12372 MauerWaechst(x, y);
12373 else if (element == EL_EXPANDABLE_WALL ||
12374 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12375 element == EL_EXPANDABLE_WALL_VERTICAL ||
12376 element == EL_EXPANDABLE_WALL_ANY ||
12377 element == EL_BD_EXPANDABLE_WALL)
12378 MauerAbleger(x, y);
12379 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12380 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12381 element == EL_EXPANDABLE_STEELWALL_ANY)
12382 MauerAblegerStahl(x, y);
12383 else if (element == EL_FLAMES)
12384 CheckForDragon(x, y);
12385 else if (element == EL_EXPLOSION)
12386 ; /* drawing of correct explosion animation is handled separately */
12387 else if (element == EL_ELEMENT_SNAPPING ||
12388 element == EL_DIAGONAL_SHRINKING ||
12389 element == EL_DIAGONAL_GROWING)
12391 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12393 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12395 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12396 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12398 #endif // ---------------------------------------------------------------------
12400 if (IS_BELT_ACTIVE(element))
12401 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12403 if (game.magic_wall_active)
12405 int jx = local_player->jx, jy = local_player->jy;
12407 /* play the element sound at the position nearest to the player */
12408 if ((element == EL_MAGIC_WALL_FULL ||
12409 element == EL_MAGIC_WALL_ACTIVE ||
12410 element == EL_MAGIC_WALL_EMPTYING ||
12411 element == EL_BD_MAGIC_WALL_FULL ||
12412 element == EL_BD_MAGIC_WALL_ACTIVE ||
12413 element == EL_BD_MAGIC_WALL_EMPTYING ||
12414 element == EL_DC_MAGIC_WALL_FULL ||
12415 element == EL_DC_MAGIC_WALL_ACTIVE ||
12416 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12417 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12426 debug_print_timestamp(0, "- time for MAIN loop: -->");
12429 #if USE_NEW_AMOEBA_CODE
12430 /* new experimental amoeba growth stuff */
12431 if (!(FrameCounter % 8))
12433 static unsigned long random = 1684108901;
12435 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12437 x = RND(lev_fieldx);
12438 y = RND(lev_fieldy);
12439 element = Feld[x][y];
12441 if (!IS_PLAYER(x,y) &&
12442 (element == EL_EMPTY ||
12443 CAN_GROW_INTO(element) ||
12444 element == EL_QUICKSAND_EMPTY ||
12445 element == EL_QUICKSAND_FAST_EMPTY ||
12446 element == EL_ACID_SPLASH_LEFT ||
12447 element == EL_ACID_SPLASH_RIGHT))
12449 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12450 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12451 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12452 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12453 Feld[x][y] = EL_AMOEBA_DROP;
12456 random = random * 129 + 1;
12462 if (game.explosions_delayed)
12465 game.explosions_delayed = FALSE;
12467 SCAN_PLAYFIELD(x, y)
12469 element = Feld[x][y];
12471 if (ExplodeField[x][y])
12472 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12473 else if (element == EL_EXPLOSION)
12474 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12476 ExplodeField[x][y] = EX_TYPE_NONE;
12479 game.explosions_delayed = TRUE;
12482 if (game.magic_wall_active)
12484 if (!(game.magic_wall_time_left % 4))
12486 int element = Feld[magic_wall_x][magic_wall_y];
12488 if (element == EL_BD_MAGIC_WALL_FULL ||
12489 element == EL_BD_MAGIC_WALL_ACTIVE ||
12490 element == EL_BD_MAGIC_WALL_EMPTYING)
12491 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12492 else if (element == EL_DC_MAGIC_WALL_FULL ||
12493 element == EL_DC_MAGIC_WALL_ACTIVE ||
12494 element == EL_DC_MAGIC_WALL_EMPTYING)
12495 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12497 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12500 if (game.magic_wall_time_left > 0)
12502 game.magic_wall_time_left--;
12504 if (!game.magic_wall_time_left)
12506 SCAN_PLAYFIELD(x, y)
12508 element = Feld[x][y];
12510 if (element == EL_MAGIC_WALL_ACTIVE ||
12511 element == EL_MAGIC_WALL_FULL)
12513 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12514 TEST_DrawLevelField(x, y);
12516 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12517 element == EL_BD_MAGIC_WALL_FULL)
12519 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12520 TEST_DrawLevelField(x, y);
12522 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12523 element == EL_DC_MAGIC_WALL_FULL)
12525 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12526 TEST_DrawLevelField(x, y);
12530 game.magic_wall_active = FALSE;
12535 if (game.light_time_left > 0)
12537 game.light_time_left--;
12539 if (game.light_time_left == 0)
12540 RedrawAllLightSwitchesAndInvisibleElements();
12543 if (game.timegate_time_left > 0)
12545 game.timegate_time_left--;
12547 if (game.timegate_time_left == 0)
12548 CloseAllOpenTimegates();
12551 if (game.lenses_time_left > 0)
12553 game.lenses_time_left--;
12555 if (game.lenses_time_left == 0)
12556 RedrawAllInvisibleElementsForLenses();
12559 if (game.magnify_time_left > 0)
12561 game.magnify_time_left--;
12563 if (game.magnify_time_left == 0)
12564 RedrawAllInvisibleElementsForMagnifier();
12567 for (i = 0; i < MAX_PLAYERS; i++)
12569 struct PlayerInfo *player = &stored_player[i];
12571 if (SHIELD_ON(player))
12573 if (player->shield_deadly_time_left)
12574 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12575 else if (player->shield_normal_time_left)
12576 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12580 #if USE_DELAYED_GFX_REDRAW
12581 SCAN_PLAYFIELD(x, y)
12584 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12586 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12587 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12590 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12591 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12593 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12594 DrawLevelField(x, y);
12596 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12597 DrawLevelFieldCrumbledSand(x, y);
12599 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12600 DrawLevelFieldCrumbledSandNeighbours(x, y);
12602 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12603 DrawTwinkleOnField(x, y);
12606 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12613 PlayAllPlayersSound();
12615 if (options.debug) /* calculate frames per second */
12617 static unsigned long fps_counter = 0;
12618 static int fps_frames = 0;
12619 unsigned long fps_delay_ms = Counter() - fps_counter;
12623 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12625 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12628 fps_counter = Counter();
12631 redraw_mask |= REDRAW_FPS;
12634 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12636 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12638 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12640 local_player->show_envelope = 0;
12644 debug_print_timestamp(0, "stop main loop profiling ");
12645 printf("----------------------------------------------------------\n");
12648 /* use random number generator in every frame to make it less predictable */
12649 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12653 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12655 int min_x = x, min_y = y, max_x = x, max_y = y;
12658 for (i = 0; i < MAX_PLAYERS; i++)
12660 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12662 if (!stored_player[i].active || &stored_player[i] == player)
12665 min_x = MIN(min_x, jx);
12666 min_y = MIN(min_y, jy);
12667 max_x = MAX(max_x, jx);
12668 max_y = MAX(max_y, jy);
12671 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12674 static boolean AllPlayersInVisibleScreen()
12678 for (i = 0; i < MAX_PLAYERS; i++)
12680 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12682 if (!stored_player[i].active)
12685 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12692 void ScrollLevel(int dx, int dy)
12695 /* (directly solved in BlitBitmap() now) */
12696 static Bitmap *bitmap_db_field2 = NULL;
12697 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12704 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12705 /* only horizontal XOR vertical scroll direction allowed */
12706 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12711 /* (directly solved in BlitBitmap() now) */
12712 if (bitmap_db_field2 == NULL)
12713 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12715 /* needed when blitting directly to same bitmap -- should not be needed with
12716 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12717 BlitBitmap(drawto_field, bitmap_db_field2,
12718 FX + TILEX * (dx == -1) - softscroll_offset,
12719 FY + TILEY * (dy == -1) - softscroll_offset,
12720 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12721 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12722 FX + TILEX * (dx == 1) - softscroll_offset,
12723 FY + TILEY * (dy == 1) - softscroll_offset);
12724 BlitBitmap(bitmap_db_field2, drawto_field,
12725 FX + TILEX * (dx == 1) - softscroll_offset,
12726 FY + TILEY * (dy == 1) - softscroll_offset,
12727 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12728 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12729 FX + TILEX * (dx == 1) - softscroll_offset,
12730 FY + TILEY * (dy == 1) - softscroll_offset);
12735 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12736 int xsize = (BX2 - BX1 + 1);
12737 int ysize = (BY2 - BY1 + 1);
12738 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12739 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12740 int step = (start < end ? +1 : -1);
12742 for (i = start; i != end; i += step)
12744 BlitBitmap(drawto_field, drawto_field,
12745 FX + TILEX * (dx != 0 ? i + step : 0),
12746 FY + TILEY * (dy != 0 ? i + step : 0),
12747 TILEX * (dx != 0 ? 1 : xsize),
12748 TILEY * (dy != 0 ? 1 : ysize),
12749 FX + TILEX * (dx != 0 ? i : 0),
12750 FY + TILEY * (dy != 0 ? i : 0));
12755 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12757 BlitBitmap(drawto_field, drawto_field,
12758 FX + TILEX * (dx == -1) - softscroll_offset,
12759 FY + TILEY * (dy == -1) - softscroll_offset,
12760 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12761 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12762 FX + TILEX * (dx == 1) - softscroll_offset,
12763 FY + TILEY * (dy == 1) - softscroll_offset);
12769 x = (dx == 1 ? BX1 : BX2);
12770 for (y = BY1; y <= BY2; y++)
12771 DrawScreenField(x, y);
12776 y = (dy == 1 ? BY1 : BY2);
12777 for (x = BX1; x <= BX2; x++)
12778 DrawScreenField(x, y);
12781 redraw_mask |= REDRAW_FIELD;
12784 static boolean canFallDown(struct PlayerInfo *player)
12786 int jx = player->jx, jy = player->jy;
12788 return (IN_LEV_FIELD(jx, jy + 1) &&
12789 (IS_FREE(jx, jy + 1) ||
12790 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12791 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12792 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12795 static boolean canPassField(int x, int y, int move_dir)
12797 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12798 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12799 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12800 int nextx = x + dx;
12801 int nexty = y + dy;
12802 int element = Feld[x][y];
12804 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12805 !CAN_MOVE(element) &&
12806 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12807 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12808 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12811 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12813 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12814 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12815 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12819 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12820 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12821 (IS_DIGGABLE(Feld[newx][newy]) ||
12822 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12823 canPassField(newx, newy, move_dir)));
12826 static void CheckGravityMovement(struct PlayerInfo *player)
12828 #if USE_PLAYER_GRAVITY
12829 if (player->gravity && !player->programmed_action)
12831 if (game.gravity && !player->programmed_action)
12834 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12835 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12836 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12837 int jx = player->jx, jy = player->jy;
12838 boolean player_is_moving_to_valid_field =
12839 (!player_is_snapping &&
12840 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12841 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12842 boolean player_can_fall_down = canFallDown(player);
12844 if (player_can_fall_down &&
12845 !player_is_moving_to_valid_field)
12846 player->programmed_action = MV_DOWN;
12850 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12852 return CheckGravityMovement(player);
12854 #if USE_PLAYER_GRAVITY
12855 if (player->gravity && !player->programmed_action)
12857 if (game.gravity && !player->programmed_action)
12860 int jx = player->jx, jy = player->jy;
12861 boolean field_under_player_is_free =
12862 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12863 boolean player_is_standing_on_valid_field =
12864 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12865 (IS_WALKABLE(Feld[jx][jy]) &&
12866 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12868 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12869 player->programmed_action = MV_DOWN;
12874 MovePlayerOneStep()
12875 -----------------------------------------------------------------------------
12876 dx, dy: direction (non-diagonal) to try to move the player to
12877 real_dx, real_dy: direction as read from input device (can be diagonal)
12880 boolean MovePlayerOneStep(struct PlayerInfo *player,
12881 int dx, int dy, int real_dx, int real_dy)
12883 int jx = player->jx, jy = player->jy;
12884 int new_jx = jx + dx, new_jy = jy + dy;
12885 #if !USE_FIXED_DONT_RUN_INTO
12889 boolean player_can_move = !player->cannot_move;
12891 if (!player->active || (!dx && !dy))
12892 return MP_NO_ACTION;
12894 player->MovDir = (dx < 0 ? MV_LEFT :
12895 dx > 0 ? MV_RIGHT :
12897 dy > 0 ? MV_DOWN : MV_NONE);
12899 if (!IN_LEV_FIELD(new_jx, new_jy))
12900 return MP_NO_ACTION;
12902 if (!player_can_move)
12904 if (player->MovPos == 0)
12906 player->is_moving = FALSE;
12907 player->is_digging = FALSE;
12908 player->is_collecting = FALSE;
12909 player->is_snapping = FALSE;
12910 player->is_pushing = FALSE;
12915 if (!options.network && game.centered_player_nr == -1 &&
12916 !AllPlayersInSight(player, new_jx, new_jy))
12917 return MP_NO_ACTION;
12919 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12920 return MP_NO_ACTION;
12923 #if !USE_FIXED_DONT_RUN_INTO
12924 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12926 /* (moved to DigField()) */
12927 if (player_can_move && DONT_RUN_INTO(element))
12929 if (element == EL_ACID && dx == 0 && dy == 1)
12931 SplashAcid(new_jx, new_jy);
12932 Feld[jx][jy] = EL_PLAYER_1;
12933 InitMovingField(jx, jy, MV_DOWN);
12934 Store[jx][jy] = EL_ACID;
12935 ContinueMoving(jx, jy);
12936 BuryPlayer(player);
12939 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12945 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12946 if (can_move != MP_MOVING)
12949 /* check if DigField() has caused relocation of the player */
12950 if (player->jx != jx || player->jy != jy)
12951 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12953 StorePlayer[jx][jy] = 0;
12954 player->last_jx = jx;
12955 player->last_jy = jy;
12956 player->jx = new_jx;
12957 player->jy = new_jy;
12958 StorePlayer[new_jx][new_jy] = player->element_nr;
12960 if (player->move_delay_value_next != -1)
12962 player->move_delay_value = player->move_delay_value_next;
12963 player->move_delay_value_next = -1;
12967 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12969 player->step_counter++;
12971 PlayerVisit[jx][jy] = FrameCounter;
12973 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12974 player->is_moving = TRUE;
12978 /* should better be called in MovePlayer(), but this breaks some tapes */
12979 ScrollPlayer(player, SCROLL_INIT);
12985 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12987 int jx = player->jx, jy = player->jy;
12988 int old_jx = jx, old_jy = jy;
12989 int moved = MP_NO_ACTION;
12991 if (!player->active)
12996 if (player->MovPos == 0)
12998 player->is_moving = FALSE;
12999 player->is_digging = FALSE;
13000 player->is_collecting = FALSE;
13001 player->is_snapping = FALSE;
13002 player->is_pushing = FALSE;
13008 if (player->move_delay > 0)
13011 player->move_delay = -1; /* set to "uninitialized" value */
13013 /* store if player is automatically moved to next field */
13014 player->is_auto_moving = (player->programmed_action != MV_NONE);
13016 /* remove the last programmed player action */
13017 player->programmed_action = 0;
13019 if (player->MovPos)
13021 /* should only happen if pre-1.2 tape recordings are played */
13022 /* this is only for backward compatibility */
13024 int original_move_delay_value = player->move_delay_value;
13027 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13031 /* scroll remaining steps with finest movement resolution */
13032 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13034 while (player->MovPos)
13036 ScrollPlayer(player, SCROLL_GO_ON);
13037 ScrollScreen(NULL, SCROLL_GO_ON);
13039 AdvanceFrameAndPlayerCounters(player->index_nr);
13045 player->move_delay_value = original_move_delay_value;
13048 player->is_active = FALSE;
13050 if (player->last_move_dir & MV_HORIZONTAL)
13052 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13053 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13057 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13058 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13061 #if USE_FIXED_BORDER_RUNNING_GFX
13062 if (!moved && !player->is_active)
13064 player->is_moving = FALSE;
13065 player->is_digging = FALSE;
13066 player->is_collecting = FALSE;
13067 player->is_snapping = FALSE;
13068 player->is_pushing = FALSE;
13076 if (moved & MP_MOVING && !ScreenMovPos &&
13077 (player->index_nr == game.centered_player_nr ||
13078 game.centered_player_nr == -1))
13080 if (moved & MP_MOVING && !ScreenMovPos &&
13081 (player == local_player || !options.network))
13084 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13085 int offset = game.scroll_delay_value;
13087 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13089 /* actual player has left the screen -- scroll in that direction */
13090 if (jx != old_jx) /* player has moved horizontally */
13091 scroll_x += (jx - old_jx);
13092 else /* player has moved vertically */
13093 scroll_y += (jy - old_jy);
13097 if (jx != old_jx) /* player has moved horizontally */
13099 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13100 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13101 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13103 /* don't scroll over playfield boundaries */
13104 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13105 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13107 /* don't scroll more than one field at a time */
13108 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13110 /* don't scroll against the player's moving direction */
13111 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13112 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13113 scroll_x = old_scroll_x;
13115 else /* player has moved vertically */
13117 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13118 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13119 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13121 /* don't scroll over playfield boundaries */
13122 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13123 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13125 /* don't scroll more than one field at a time */
13126 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13128 /* don't scroll against the player's moving direction */
13129 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13130 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13131 scroll_y = old_scroll_y;
13135 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13138 if (!options.network && game.centered_player_nr == -1 &&
13139 !AllPlayersInVisibleScreen())
13141 scroll_x = old_scroll_x;
13142 scroll_y = old_scroll_y;
13146 if (!options.network && !AllPlayersInVisibleScreen())
13148 scroll_x = old_scroll_x;
13149 scroll_y = old_scroll_y;
13154 ScrollScreen(player, SCROLL_INIT);
13155 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13160 player->StepFrame = 0;
13162 if (moved & MP_MOVING)
13164 if (old_jx != jx && old_jy == jy)
13165 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13166 else if (old_jx == jx && old_jy != jy)
13167 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13169 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13171 player->last_move_dir = player->MovDir;
13172 player->is_moving = TRUE;
13173 player->is_snapping = FALSE;
13174 player->is_switching = FALSE;
13175 player->is_dropping = FALSE;
13176 player->is_dropping_pressed = FALSE;
13177 player->drop_pressed_delay = 0;
13180 /* should better be called here than above, but this breaks some tapes */
13181 ScrollPlayer(player, SCROLL_INIT);
13186 CheckGravityMovementWhenNotMoving(player);
13188 player->is_moving = FALSE;
13190 /* at this point, the player is allowed to move, but cannot move right now
13191 (e.g. because of something blocking the way) -- ensure that the player
13192 is also allowed to move in the next frame (in old versions before 3.1.1,
13193 the player was forced to wait again for eight frames before next try) */
13195 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13196 player->move_delay = 0; /* allow direct movement in the next frame */
13199 if (player->move_delay == -1) /* not yet initialized by DigField() */
13200 player->move_delay = player->move_delay_value;
13202 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13204 TestIfPlayerTouchesBadThing(jx, jy);
13205 TestIfPlayerTouchesCustomElement(jx, jy);
13208 if (!player->active)
13209 RemovePlayer(player);
13214 void ScrollPlayer(struct PlayerInfo *player, int mode)
13216 int jx = player->jx, jy = player->jy;
13217 int last_jx = player->last_jx, last_jy = player->last_jy;
13218 int move_stepsize = TILEX / player->move_delay_value;
13220 #if USE_NEW_PLAYER_SPEED
13221 if (!player->active)
13224 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13227 if (!player->active || player->MovPos == 0)
13231 if (mode == SCROLL_INIT)
13233 player->actual_frame_counter = FrameCounter;
13234 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13236 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13237 Feld[last_jx][last_jy] == EL_EMPTY)
13239 int last_field_block_delay = 0; /* start with no blocking at all */
13240 int block_delay_adjustment = player->block_delay_adjustment;
13242 /* if player blocks last field, add delay for exactly one move */
13243 if (player->block_last_field)
13245 last_field_block_delay += player->move_delay_value;
13247 /* when blocking enabled, prevent moving up despite gravity */
13248 #if USE_PLAYER_GRAVITY
13249 if (player->gravity && player->MovDir == MV_UP)
13250 block_delay_adjustment = -1;
13252 if (game.gravity && player->MovDir == MV_UP)
13253 block_delay_adjustment = -1;
13257 /* add block delay adjustment (also possible when not blocking) */
13258 last_field_block_delay += block_delay_adjustment;
13260 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13261 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13264 #if USE_NEW_PLAYER_SPEED
13265 if (player->MovPos != 0) /* player has not yet reached destination */
13271 else if (!FrameReached(&player->actual_frame_counter, 1))
13274 #if USE_NEW_PLAYER_SPEED
13275 if (player->MovPos != 0)
13277 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13278 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13280 /* before DrawPlayer() to draw correct player graphic for this case */
13281 if (player->MovPos == 0)
13282 CheckGravityMovement(player);
13285 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13286 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13288 /* before DrawPlayer() to draw correct player graphic for this case */
13289 if (player->MovPos == 0)
13290 CheckGravityMovement(player);
13293 if (player->MovPos == 0) /* player reached destination field */
13295 if (player->move_delay_reset_counter > 0)
13297 player->move_delay_reset_counter--;
13299 if (player->move_delay_reset_counter == 0)
13301 /* continue with normal speed after quickly moving through gate */
13302 HALVE_PLAYER_SPEED(player);
13304 /* be able to make the next move without delay */
13305 player->move_delay = 0;
13309 player->last_jx = jx;
13310 player->last_jy = jy;
13312 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13313 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13314 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13315 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13316 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13317 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13319 DrawPlayer(player); /* needed here only to cleanup last field */
13320 RemovePlayer(player);
13322 if (local_player->friends_still_needed == 0 ||
13323 IS_SP_ELEMENT(Feld[jx][jy]))
13324 PlayerWins(player);
13327 /* this breaks one level: "machine", level 000 */
13329 int move_direction = player->MovDir;
13330 int enter_side = MV_DIR_OPPOSITE(move_direction);
13331 int leave_side = move_direction;
13332 int old_jx = last_jx;
13333 int old_jy = last_jy;
13334 int old_element = Feld[old_jx][old_jy];
13335 int new_element = Feld[jx][jy];
13337 if (IS_CUSTOM_ELEMENT(old_element))
13338 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13340 player->index_bit, leave_side);
13342 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13343 CE_PLAYER_LEAVES_X,
13344 player->index_bit, leave_side);
13346 if (IS_CUSTOM_ELEMENT(new_element))
13347 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13348 player->index_bit, enter_side);
13350 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13351 CE_PLAYER_ENTERS_X,
13352 player->index_bit, enter_side);
13354 #if USE_FIX_CE_ACTION_WITH_PLAYER
13355 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13356 CE_MOVE_OF_X, move_direction);
13358 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13359 CE_MOVE_OF_X, move_direction);
13363 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13365 TestIfPlayerTouchesBadThing(jx, jy);
13366 TestIfPlayerTouchesCustomElement(jx, jy);
13368 /* needed because pushed element has not yet reached its destination,
13369 so it would trigger a change event at its previous field location */
13370 if (!player->is_pushing)
13371 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13373 if (!player->active)
13374 RemovePlayer(player);
13377 if (!local_player->LevelSolved && level.use_step_counter)
13387 if (TimeLeft <= 10 && setup.time_limit)
13388 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13391 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13393 DisplayGameControlValues();
13395 DrawGameValue_Time(TimeLeft);
13398 if (!TimeLeft && setup.time_limit)
13399 for (i = 0; i < MAX_PLAYERS; i++)
13400 KillPlayer(&stored_player[i]);
13403 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13405 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13407 DisplayGameControlValues();
13410 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13411 DrawGameValue_Time(TimePlayed);
13415 if (tape.single_step && tape.recording && !tape.pausing &&
13416 !player->programmed_action)
13417 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13421 void ScrollScreen(struct PlayerInfo *player, int mode)
13423 static unsigned long screen_frame_counter = 0;
13425 if (mode == SCROLL_INIT)
13427 /* set scrolling step size according to actual player's moving speed */
13428 ScrollStepSize = TILEX / player->move_delay_value;
13430 screen_frame_counter = FrameCounter;
13431 ScreenMovDir = player->MovDir;
13432 ScreenMovPos = player->MovPos;
13433 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13436 else if (!FrameReached(&screen_frame_counter, 1))
13441 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13442 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13443 redraw_mask |= REDRAW_FIELD;
13446 ScreenMovDir = MV_NONE;
13449 void TestIfPlayerTouchesCustomElement(int x, int y)
13451 static int xy[4][2] =
13458 static int trigger_sides[4][2] =
13460 /* center side border side */
13461 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13462 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13463 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13464 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13466 static int touch_dir[4] =
13468 MV_LEFT | MV_RIGHT,
13473 int center_element = Feld[x][y]; /* should always be non-moving! */
13476 for (i = 0; i < NUM_DIRECTIONS; i++)
13478 int xx = x + xy[i][0];
13479 int yy = y + xy[i][1];
13480 int center_side = trigger_sides[i][0];
13481 int border_side = trigger_sides[i][1];
13482 int border_element;
13484 if (!IN_LEV_FIELD(xx, yy))
13487 if (IS_PLAYER(x, y)) /* player found at center element */
13489 struct PlayerInfo *player = PLAYERINFO(x, y);
13491 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13492 border_element = Feld[xx][yy]; /* may be moving! */
13493 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13494 border_element = Feld[xx][yy];
13495 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13496 border_element = MovingOrBlocked2Element(xx, yy);
13498 continue; /* center and border element do not touch */
13500 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13501 player->index_bit, border_side);
13502 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13503 CE_PLAYER_TOUCHES_X,
13504 player->index_bit, border_side);
13506 #if USE_FIX_CE_ACTION_WITH_PLAYER
13508 /* use player element that is initially defined in the level playfield,
13509 not the player element that corresponds to the runtime player number
13510 (example: a level that contains EL_PLAYER_3 as the only player would
13511 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13512 int player_element = PLAYERINFO(x, y)->initial_element;
13514 CheckElementChangeBySide(xx, yy, border_element, player_element,
13515 CE_TOUCHING_X, border_side);
13519 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13521 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13523 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13525 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13526 continue; /* center and border element do not touch */
13529 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13530 player->index_bit, center_side);
13531 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13532 CE_PLAYER_TOUCHES_X,
13533 player->index_bit, center_side);
13535 #if USE_FIX_CE_ACTION_WITH_PLAYER
13537 /* use player element that is initially defined in the level playfield,
13538 not the player element that corresponds to the runtime player number
13539 (example: a level that contains EL_PLAYER_3 as the only player would
13540 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13541 int player_element = PLAYERINFO(xx, yy)->initial_element;
13543 CheckElementChangeBySide(x, y, center_element, player_element,
13544 CE_TOUCHING_X, center_side);
13553 #if USE_ELEMENT_TOUCHING_BUGFIX
13555 void TestIfElementTouchesCustomElement(int x, int y)
13557 static int xy[4][2] =
13564 static int trigger_sides[4][2] =
13566 /* center side border side */
13567 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13568 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13569 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13570 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13572 static int touch_dir[4] =
13574 MV_LEFT | MV_RIGHT,
13579 boolean change_center_element = FALSE;
13580 int center_element = Feld[x][y]; /* should always be non-moving! */
13581 int border_element_old[NUM_DIRECTIONS];
13584 for (i = 0; i < NUM_DIRECTIONS; i++)
13586 int xx = x + xy[i][0];
13587 int yy = y + xy[i][1];
13588 int border_element;
13590 border_element_old[i] = -1;
13592 if (!IN_LEV_FIELD(xx, yy))
13595 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13596 border_element = Feld[xx][yy]; /* may be moving! */
13597 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13598 border_element = Feld[xx][yy];
13599 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13600 border_element = MovingOrBlocked2Element(xx, yy);
13602 continue; /* center and border element do not touch */
13604 border_element_old[i] = border_element;
13607 for (i = 0; i < NUM_DIRECTIONS; i++)
13609 int xx = x + xy[i][0];
13610 int yy = y + xy[i][1];
13611 int center_side = trigger_sides[i][0];
13612 int border_element = border_element_old[i];
13614 if (border_element == -1)
13617 /* check for change of border element */
13618 CheckElementChangeBySide(xx, yy, border_element, center_element,
13619 CE_TOUCHING_X, center_side);
13621 /* (center element cannot be player, so we dont have to check this here) */
13624 for (i = 0; i < NUM_DIRECTIONS; i++)
13626 int xx = x + xy[i][0];
13627 int yy = y + xy[i][1];
13628 int border_side = trigger_sides[i][1];
13629 int border_element = border_element_old[i];
13631 if (border_element == -1)
13634 /* check for change of center element (but change it only once) */
13635 if (!change_center_element)
13636 change_center_element =
13637 CheckElementChangeBySide(x, y, center_element, border_element,
13638 CE_TOUCHING_X, border_side);
13640 #if USE_FIX_CE_ACTION_WITH_PLAYER
13641 if (IS_PLAYER(xx, yy))
13643 /* use player element that is initially defined in the level playfield,
13644 not the player element that corresponds to the runtime player number
13645 (example: a level that contains EL_PLAYER_3 as the only player would
13646 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13647 int player_element = PLAYERINFO(xx, yy)->initial_element;
13649 CheckElementChangeBySide(x, y, center_element, player_element,
13650 CE_TOUCHING_X, border_side);
13658 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13660 static int xy[4][2] =
13667 static int trigger_sides[4][2] =
13669 /* center side border side */
13670 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13671 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13672 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13673 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13675 static int touch_dir[4] =
13677 MV_LEFT | MV_RIGHT,
13682 boolean change_center_element = FALSE;
13683 int center_element = Feld[x][y]; /* should always be non-moving! */
13686 for (i = 0; i < NUM_DIRECTIONS; i++)
13688 int xx = x + xy[i][0];
13689 int yy = y + xy[i][1];
13690 int center_side = trigger_sides[i][0];
13691 int border_side = trigger_sides[i][1];
13692 int border_element;
13694 if (!IN_LEV_FIELD(xx, yy))
13697 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13698 border_element = Feld[xx][yy]; /* may be moving! */
13699 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13700 border_element = Feld[xx][yy];
13701 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13702 border_element = MovingOrBlocked2Element(xx, yy);
13704 continue; /* center and border element do not touch */
13706 /* check for change of center element (but change it only once) */
13707 if (!change_center_element)
13708 change_center_element =
13709 CheckElementChangeBySide(x, y, center_element, border_element,
13710 CE_TOUCHING_X, border_side);
13712 /* check for change of border element */
13713 CheckElementChangeBySide(xx, yy, border_element, center_element,
13714 CE_TOUCHING_X, center_side);
13720 void TestIfElementHitsCustomElement(int x, int y, int direction)
13722 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13723 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13724 int hitx = x + dx, hity = y + dy;
13725 int hitting_element = Feld[x][y];
13726 int touched_element;
13728 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13731 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13732 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13734 if (IN_LEV_FIELD(hitx, hity))
13736 int opposite_direction = MV_DIR_OPPOSITE(direction);
13737 int hitting_side = direction;
13738 int touched_side = opposite_direction;
13739 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13740 MovDir[hitx][hity] != direction ||
13741 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13747 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13748 CE_HITTING_X, touched_side);
13750 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13751 CE_HIT_BY_X, hitting_side);
13753 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13754 CE_HIT_BY_SOMETHING, opposite_direction);
13756 #if USE_FIX_CE_ACTION_WITH_PLAYER
13757 if (IS_PLAYER(hitx, hity))
13759 /* use player element that is initially defined in the level playfield,
13760 not the player element that corresponds to the runtime player number
13761 (example: a level that contains EL_PLAYER_3 as the only player would
13762 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13763 int player_element = PLAYERINFO(hitx, hity)->initial_element;
13765 CheckElementChangeBySide(x, y, hitting_element, player_element,
13766 CE_HITTING_X, touched_side);
13772 /* "hitting something" is also true when hitting the playfield border */
13773 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13774 CE_HITTING_SOMETHING, direction);
13778 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13780 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13781 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13782 int hitx = x + dx, hity = y + dy;
13783 int hitting_element = Feld[x][y];
13784 int touched_element;
13786 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13787 !IS_FREE(hitx, hity) &&
13788 (!IS_MOVING(hitx, hity) ||
13789 MovDir[hitx][hity] != direction ||
13790 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13793 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13797 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13801 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13802 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13804 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13805 EP_CAN_SMASH_EVERYTHING, direction);
13807 if (IN_LEV_FIELD(hitx, hity))
13809 int opposite_direction = MV_DIR_OPPOSITE(direction);
13810 int hitting_side = direction;
13811 int touched_side = opposite_direction;
13813 int touched_element = MovingOrBlocked2Element(hitx, hity);
13816 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13817 MovDir[hitx][hity] != direction ||
13818 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13827 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13828 CE_SMASHED_BY_SOMETHING, opposite_direction);
13830 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13831 CE_OTHER_IS_SMASHING, touched_side);
13833 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13834 CE_OTHER_GETS_SMASHED, hitting_side);
13840 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13842 int i, kill_x = -1, kill_y = -1;
13844 int bad_element = -1;
13845 static int test_xy[4][2] =
13852 static int test_dir[4] =
13860 for (i = 0; i < NUM_DIRECTIONS; i++)
13862 int test_x, test_y, test_move_dir, test_element;
13864 test_x = good_x + test_xy[i][0];
13865 test_y = good_y + test_xy[i][1];
13867 if (!IN_LEV_FIELD(test_x, test_y))
13871 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13873 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13875 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13876 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13878 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13879 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13883 bad_element = test_element;
13889 if (kill_x != -1 || kill_y != -1)
13891 if (IS_PLAYER(good_x, good_y))
13893 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13895 if (player->shield_deadly_time_left > 0 &&
13896 !IS_INDESTRUCTIBLE(bad_element))
13897 Bang(kill_x, kill_y);
13898 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13899 KillPlayer(player);
13902 Bang(good_x, good_y);
13906 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13908 int i, kill_x = -1, kill_y = -1;
13909 int bad_element = Feld[bad_x][bad_y];
13910 static int test_xy[4][2] =
13917 static int touch_dir[4] =
13919 MV_LEFT | MV_RIGHT,
13924 static int test_dir[4] =
13932 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13935 for (i = 0; i < NUM_DIRECTIONS; i++)
13937 int test_x, test_y, test_move_dir, test_element;
13939 test_x = bad_x + test_xy[i][0];
13940 test_y = bad_y + test_xy[i][1];
13942 if (!IN_LEV_FIELD(test_x, test_y))
13946 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13948 test_element = Feld[test_x][test_y];
13950 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13951 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13953 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13954 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13956 /* good thing is player or penguin that does not move away */
13957 if (IS_PLAYER(test_x, test_y))
13959 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13961 if (bad_element == EL_ROBOT && player->is_moving)
13962 continue; /* robot does not kill player if he is moving */
13964 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13966 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13967 continue; /* center and border element do not touch */
13975 else if (test_element == EL_PENGUIN)
13985 if (kill_x != -1 || kill_y != -1)
13987 if (IS_PLAYER(kill_x, kill_y))
13989 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13991 if (player->shield_deadly_time_left > 0 &&
13992 !IS_INDESTRUCTIBLE(bad_element))
13993 Bang(bad_x, bad_y);
13994 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13995 KillPlayer(player);
13998 Bang(kill_x, kill_y);
14002 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14004 int bad_element = Feld[bad_x][bad_y];
14005 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14006 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14007 int test_x = bad_x + dx, test_y = bad_y + dy;
14008 int test_move_dir, test_element;
14009 int kill_x = -1, kill_y = -1;
14011 if (!IN_LEV_FIELD(test_x, test_y))
14015 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14017 test_element = Feld[test_x][test_y];
14019 if (test_move_dir != bad_move_dir)
14021 /* good thing can be player or penguin that does not move away */
14022 if (IS_PLAYER(test_x, test_y))
14024 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14026 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14027 player as being hit when he is moving towards the bad thing, because
14028 the "get hit by" condition would be lost after the player stops) */
14029 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14030 return; /* player moves away from bad thing */
14035 else if (test_element == EL_PENGUIN)
14042 if (kill_x != -1 || kill_y != -1)
14044 if (IS_PLAYER(kill_x, kill_y))
14046 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14048 if (player->shield_deadly_time_left > 0 &&
14049 !IS_INDESTRUCTIBLE(bad_element))
14050 Bang(bad_x, bad_y);
14051 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14052 KillPlayer(player);
14055 Bang(kill_x, kill_y);
14059 void TestIfPlayerTouchesBadThing(int x, int y)
14061 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14064 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14066 TestIfGoodThingHitsBadThing(x, y, move_dir);
14069 void TestIfBadThingTouchesPlayer(int x, int y)
14071 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14074 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14076 TestIfBadThingHitsGoodThing(x, y, move_dir);
14079 void TestIfFriendTouchesBadThing(int x, int y)
14081 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14084 void TestIfBadThingTouchesFriend(int x, int y)
14086 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14089 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14091 int i, kill_x = bad_x, kill_y = bad_y;
14092 static int xy[4][2] =
14100 for (i = 0; i < NUM_DIRECTIONS; i++)
14104 x = bad_x + xy[i][0];
14105 y = bad_y + xy[i][1];
14106 if (!IN_LEV_FIELD(x, y))
14109 element = Feld[x][y];
14110 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14111 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14119 if (kill_x != bad_x || kill_y != bad_y)
14120 Bang(bad_x, bad_y);
14123 void KillPlayer(struct PlayerInfo *player)
14125 int jx = player->jx, jy = player->jy;
14127 if (!player->active)
14130 /* the following code was introduced to prevent an infinite loop when calling
14132 -> CheckTriggeredElementChangeExt()
14133 -> ExecuteCustomElementAction()
14135 -> (infinitely repeating the above sequence of function calls)
14136 which occurs when killing the player while having a CE with the setting
14137 "kill player X when explosion of <player X>"; the solution using a new
14138 field "player->killed" was chosen for backwards compatibility, although
14139 clever use of the fields "player->active" etc. would probably also work */
14141 if (player->killed)
14145 player->killed = TRUE;
14147 /* remove accessible field at the player's position */
14148 Feld[jx][jy] = EL_EMPTY;
14150 /* deactivate shield (else Bang()/Explode() would not work right) */
14151 player->shield_normal_time_left = 0;
14152 player->shield_deadly_time_left = 0;
14156 #if USE_PLAYER_REANIMATION
14157 if (player->killed) /* player may have been reanimated */
14158 BuryPlayer(player);
14160 BuryPlayer(player);
14164 static void KillPlayerUnlessEnemyProtected(int x, int y)
14166 if (!PLAYER_ENEMY_PROTECTED(x, y))
14167 KillPlayer(PLAYERINFO(x, y));
14170 static void KillPlayerUnlessExplosionProtected(int x, int y)
14172 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14173 KillPlayer(PLAYERINFO(x, y));
14176 void BuryPlayer(struct PlayerInfo *player)
14178 int jx = player->jx, jy = player->jy;
14180 if (!player->active)
14183 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14184 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14186 player->GameOver = TRUE;
14187 RemovePlayer(player);
14190 void RemovePlayer(struct PlayerInfo *player)
14192 int jx = player->jx, jy = player->jy;
14193 int i, found = FALSE;
14195 player->present = FALSE;
14196 player->active = FALSE;
14198 if (!ExplodeField[jx][jy])
14199 StorePlayer[jx][jy] = 0;
14201 if (player->is_moving)
14202 TEST_DrawLevelField(player->last_jx, player->last_jy);
14204 for (i = 0; i < MAX_PLAYERS; i++)
14205 if (stored_player[i].active)
14209 AllPlayersGone = TRUE;
14215 #if USE_NEW_SNAP_DELAY
14216 static void setFieldForSnapping(int x, int y, int element, int direction)
14218 struct ElementInfo *ei = &element_info[element];
14219 int direction_bit = MV_DIR_TO_BIT(direction);
14220 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14221 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14222 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14224 Feld[x][y] = EL_ELEMENT_SNAPPING;
14225 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14227 ResetGfxAnimation(x, y);
14229 GfxElement[x][y] = element;
14230 GfxAction[x][y] = action;
14231 GfxDir[x][y] = direction;
14232 GfxFrame[x][y] = -1;
14237 =============================================================================
14238 checkDiagonalPushing()
14239 -----------------------------------------------------------------------------
14240 check if diagonal input device direction results in pushing of object
14241 (by checking if the alternative direction is walkable, diggable, ...)
14242 =============================================================================
14245 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14246 int x, int y, int real_dx, int real_dy)
14248 int jx, jy, dx, dy, xx, yy;
14250 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14253 /* diagonal direction: check alternative direction */
14258 xx = jx + (dx == 0 ? real_dx : 0);
14259 yy = jy + (dy == 0 ? real_dy : 0);
14261 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14265 =============================================================================
14267 -----------------------------------------------------------------------------
14268 x, y: field next to player (non-diagonal) to try to dig to
14269 real_dx, real_dy: direction as read from input device (can be diagonal)
14270 =============================================================================
14273 static int DigField(struct PlayerInfo *player,
14274 int oldx, int oldy, int x, int y,
14275 int real_dx, int real_dy, int mode)
14277 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14278 boolean player_was_pushing = player->is_pushing;
14279 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14280 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14281 int jx = oldx, jy = oldy;
14282 int dx = x - jx, dy = y - jy;
14283 int nextx = x + dx, nexty = y + dy;
14284 int move_direction = (dx == -1 ? MV_LEFT :
14285 dx == +1 ? MV_RIGHT :
14287 dy == +1 ? MV_DOWN : MV_NONE);
14288 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14289 int dig_side = MV_DIR_OPPOSITE(move_direction);
14290 int old_element = Feld[jx][jy];
14291 #if USE_FIXED_DONT_RUN_INTO
14292 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14298 if (is_player) /* function can also be called by EL_PENGUIN */
14300 if (player->MovPos == 0)
14302 player->is_digging = FALSE;
14303 player->is_collecting = FALSE;
14306 if (player->MovPos == 0) /* last pushing move finished */
14307 player->is_pushing = FALSE;
14309 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14311 player->is_switching = FALSE;
14312 player->push_delay = -1;
14314 return MP_NO_ACTION;
14318 #if !USE_FIXED_DONT_RUN_INTO
14319 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14320 return MP_NO_ACTION;
14323 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14324 old_element = Back[jx][jy];
14326 /* in case of element dropped at player position, check background */
14327 else if (Back[jx][jy] != EL_EMPTY &&
14328 game.engine_version >= VERSION_IDENT(2,2,0,0))
14329 old_element = Back[jx][jy];
14331 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14332 return MP_NO_ACTION; /* field has no opening in this direction */
14334 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14335 return MP_NO_ACTION; /* field has no opening in this direction */
14337 #if USE_FIXED_DONT_RUN_INTO
14338 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14342 Feld[jx][jy] = player->artwork_element;
14343 InitMovingField(jx, jy, MV_DOWN);
14344 Store[jx][jy] = EL_ACID;
14345 ContinueMoving(jx, jy);
14346 BuryPlayer(player);
14348 return MP_DONT_RUN_INTO;
14352 #if USE_FIXED_DONT_RUN_INTO
14353 if (player_can_move && DONT_RUN_INTO(element))
14355 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14357 return MP_DONT_RUN_INTO;
14361 #if USE_FIXED_DONT_RUN_INTO
14362 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14363 return MP_NO_ACTION;
14366 #if !USE_FIXED_DONT_RUN_INTO
14367 element = Feld[x][y];
14370 collect_count = element_info[element].collect_count_initial;
14372 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14373 return MP_NO_ACTION;
14375 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14376 player_can_move = player_can_move_or_snap;
14378 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14379 game.engine_version >= VERSION_IDENT(2,2,0,0))
14381 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14382 player->index_bit, dig_side);
14383 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14384 player->index_bit, dig_side);
14386 if (element == EL_DC_LANDMINE)
14389 if (Feld[x][y] != element) /* field changed by snapping */
14392 return MP_NO_ACTION;
14395 #if USE_PLAYER_GRAVITY
14396 if (player->gravity && is_player && !player->is_auto_moving &&
14397 canFallDown(player) && move_direction != MV_DOWN &&
14398 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14399 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14401 if (game.gravity && is_player && !player->is_auto_moving &&
14402 canFallDown(player) && move_direction != MV_DOWN &&
14403 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14404 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14407 if (player_can_move &&
14408 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14410 int sound_element = SND_ELEMENT(element);
14411 int sound_action = ACTION_WALKING;
14413 if (IS_RND_GATE(element))
14415 if (!player->key[RND_GATE_NR(element)])
14416 return MP_NO_ACTION;
14418 else if (IS_RND_GATE_GRAY(element))
14420 if (!player->key[RND_GATE_GRAY_NR(element)])
14421 return MP_NO_ACTION;
14423 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14425 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14426 return MP_NO_ACTION;
14428 else if (element == EL_EXIT_OPEN ||
14429 element == EL_EM_EXIT_OPEN ||
14430 element == EL_STEEL_EXIT_OPEN ||
14431 element == EL_EM_STEEL_EXIT_OPEN ||
14432 element == EL_SP_EXIT_OPEN ||
14433 element == EL_SP_EXIT_OPENING)
14435 sound_action = ACTION_PASSING; /* player is passing exit */
14437 else if (element == EL_EMPTY)
14439 sound_action = ACTION_MOVING; /* nothing to walk on */
14442 /* play sound from background or player, whatever is available */
14443 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14444 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14446 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14448 else if (player_can_move &&
14449 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14451 if (!ACCESS_FROM(element, opposite_direction))
14452 return MP_NO_ACTION; /* field not accessible from this direction */
14454 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14455 return MP_NO_ACTION;
14457 if (IS_EM_GATE(element))
14459 if (!player->key[EM_GATE_NR(element)])
14460 return MP_NO_ACTION;
14462 else if (IS_EM_GATE_GRAY(element))
14464 if (!player->key[EM_GATE_GRAY_NR(element)])
14465 return MP_NO_ACTION;
14467 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14469 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14470 return MP_NO_ACTION;
14472 else if (IS_EMC_GATE(element))
14474 if (!player->key[EMC_GATE_NR(element)])
14475 return MP_NO_ACTION;
14477 else if (IS_EMC_GATE_GRAY(element))
14479 if (!player->key[EMC_GATE_GRAY_NR(element)])
14480 return MP_NO_ACTION;
14482 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14484 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14485 return MP_NO_ACTION;
14487 else if (element == EL_DC_GATE_WHITE ||
14488 element == EL_DC_GATE_WHITE_GRAY ||
14489 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14491 if (player->num_white_keys == 0)
14492 return MP_NO_ACTION;
14494 player->num_white_keys--;
14496 else if (IS_SP_PORT(element))
14498 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14499 element == EL_SP_GRAVITY_PORT_RIGHT ||
14500 element == EL_SP_GRAVITY_PORT_UP ||
14501 element == EL_SP_GRAVITY_PORT_DOWN)
14502 #if USE_PLAYER_GRAVITY
14503 player->gravity = !player->gravity;
14505 game.gravity = !game.gravity;
14507 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14508 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14509 element == EL_SP_GRAVITY_ON_PORT_UP ||
14510 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14511 #if USE_PLAYER_GRAVITY
14512 player->gravity = TRUE;
14514 game.gravity = TRUE;
14516 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14517 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14518 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14519 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14520 #if USE_PLAYER_GRAVITY
14521 player->gravity = FALSE;
14523 game.gravity = FALSE;
14527 /* automatically move to the next field with double speed */
14528 player->programmed_action = move_direction;
14530 if (player->move_delay_reset_counter == 0)
14532 player->move_delay_reset_counter = 2; /* two double speed steps */
14534 DOUBLE_PLAYER_SPEED(player);
14537 PlayLevelSoundAction(x, y, ACTION_PASSING);
14539 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14543 if (mode != DF_SNAP)
14545 GfxElement[x][y] = GFX_ELEMENT(element);
14546 player->is_digging = TRUE;
14549 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14551 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14552 player->index_bit, dig_side);
14554 if (mode == DF_SNAP)
14556 #if USE_NEW_SNAP_DELAY
14557 if (level.block_snap_field)
14558 setFieldForSnapping(x, y, element, move_direction);
14560 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14562 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14565 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14566 player->index_bit, dig_side);
14569 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14573 if (is_player && mode != DF_SNAP)
14575 GfxElement[x][y] = element;
14576 player->is_collecting = TRUE;
14579 if (element == EL_SPEED_PILL)
14581 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14583 else if (element == EL_EXTRA_TIME && level.time > 0)
14585 TimeLeft += level.extra_time;
14588 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14590 DisplayGameControlValues();
14592 DrawGameValue_Time(TimeLeft);
14595 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14597 player->shield_normal_time_left += level.shield_normal_time;
14598 if (element == EL_SHIELD_DEADLY)
14599 player->shield_deadly_time_left += level.shield_deadly_time;
14601 else if (element == EL_DYNAMITE ||
14602 element == EL_EM_DYNAMITE ||
14603 element == EL_SP_DISK_RED)
14605 if (player->inventory_size < MAX_INVENTORY_SIZE)
14606 player->inventory_element[player->inventory_size++] = element;
14608 DrawGameDoorValues();
14610 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14612 player->dynabomb_count++;
14613 player->dynabombs_left++;
14615 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14617 player->dynabomb_size++;
14619 else if (element == EL_DYNABOMB_INCREASE_POWER)
14621 player->dynabomb_xl = TRUE;
14623 else if (IS_KEY(element))
14625 player->key[KEY_NR(element)] = TRUE;
14627 DrawGameDoorValues();
14629 else if (element == EL_DC_KEY_WHITE)
14631 player->num_white_keys++;
14633 /* display white keys? */
14634 /* DrawGameDoorValues(); */
14636 else if (IS_ENVELOPE(element))
14638 player->show_envelope = element;
14640 else if (element == EL_EMC_LENSES)
14642 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14644 RedrawAllInvisibleElementsForLenses();
14646 else if (element == EL_EMC_MAGNIFIER)
14648 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14650 RedrawAllInvisibleElementsForMagnifier();
14652 else if (IS_DROPPABLE(element) ||
14653 IS_THROWABLE(element)) /* can be collected and dropped */
14657 if (collect_count == 0)
14658 player->inventory_infinite_element = element;
14660 for (i = 0; i < collect_count; i++)
14661 if (player->inventory_size < MAX_INVENTORY_SIZE)
14662 player->inventory_element[player->inventory_size++] = element;
14664 DrawGameDoorValues();
14666 else if (collect_count > 0)
14668 local_player->gems_still_needed -= collect_count;
14669 if (local_player->gems_still_needed < 0)
14670 local_player->gems_still_needed = 0;
14673 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14675 DisplayGameControlValues();
14677 DrawGameValue_Emeralds(local_player->gems_still_needed);
14681 RaiseScoreElement(element);
14682 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14685 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14686 player->index_bit, dig_side);
14688 if (mode == DF_SNAP)
14690 #if USE_NEW_SNAP_DELAY
14691 if (level.block_snap_field)
14692 setFieldForSnapping(x, y, element, move_direction);
14694 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14696 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14699 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14700 player->index_bit, dig_side);
14703 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14705 if (mode == DF_SNAP && element != EL_BD_ROCK)
14706 return MP_NO_ACTION;
14708 if (CAN_FALL(element) && dy)
14709 return MP_NO_ACTION;
14711 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14712 !(element == EL_SPRING && level.use_spring_bug))
14713 return MP_NO_ACTION;
14715 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14716 ((move_direction & MV_VERTICAL &&
14717 ((element_info[element].move_pattern & MV_LEFT &&
14718 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14719 (element_info[element].move_pattern & MV_RIGHT &&
14720 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14721 (move_direction & MV_HORIZONTAL &&
14722 ((element_info[element].move_pattern & MV_UP &&
14723 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14724 (element_info[element].move_pattern & MV_DOWN &&
14725 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14726 return MP_NO_ACTION;
14728 /* do not push elements already moving away faster than player */
14729 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14730 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14731 return MP_NO_ACTION;
14733 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14735 if (player->push_delay_value == -1 || !player_was_pushing)
14736 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14738 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14740 if (player->push_delay_value == -1)
14741 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14743 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14745 if (!player->is_pushing)
14746 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14749 player->is_pushing = TRUE;
14750 player->is_active = TRUE;
14752 if (!(IN_LEV_FIELD(nextx, nexty) &&
14753 (IS_FREE(nextx, nexty) ||
14754 (IS_SB_ELEMENT(element) &&
14755 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14756 (IS_CUSTOM_ELEMENT(element) &&
14757 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14758 return MP_NO_ACTION;
14760 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14761 return MP_NO_ACTION;
14763 if (player->push_delay == -1) /* new pushing; restart delay */
14764 player->push_delay = 0;
14766 if (player->push_delay < player->push_delay_value &&
14767 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14768 element != EL_SPRING && element != EL_BALLOON)
14770 /* make sure that there is no move delay before next try to push */
14771 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14772 player->move_delay = 0;
14774 return MP_NO_ACTION;
14777 if (IS_CUSTOM_ELEMENT(element) &&
14778 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14780 if (!DigFieldByCE(nextx, nexty, element))
14781 return MP_NO_ACTION;
14784 if (IS_SB_ELEMENT(element))
14786 if (element == EL_SOKOBAN_FIELD_FULL)
14788 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14789 local_player->sokobanfields_still_needed++;
14792 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14794 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14795 local_player->sokobanfields_still_needed--;
14798 Feld[x][y] = EL_SOKOBAN_OBJECT;
14800 if (Back[x][y] == Back[nextx][nexty])
14801 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14802 else if (Back[x][y] != 0)
14803 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14806 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14809 if (local_player->sokobanfields_still_needed == 0 &&
14810 game.emulation == EMU_SOKOBAN)
14812 PlayerWins(player);
14814 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14818 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14820 InitMovingField(x, y, move_direction);
14821 GfxAction[x][y] = ACTION_PUSHING;
14823 if (mode == DF_SNAP)
14824 ContinueMoving(x, y);
14826 MovPos[x][y] = (dx != 0 ? dx : dy);
14828 Pushed[x][y] = TRUE;
14829 Pushed[nextx][nexty] = TRUE;
14831 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14832 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14834 player->push_delay_value = -1; /* get new value later */
14836 /* check for element change _after_ element has been pushed */
14837 if (game.use_change_when_pushing_bug)
14839 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14840 player->index_bit, dig_side);
14841 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14842 player->index_bit, dig_side);
14845 else if (IS_SWITCHABLE(element))
14847 if (PLAYER_SWITCHING(player, x, y))
14849 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14850 player->index_bit, dig_side);
14855 player->is_switching = TRUE;
14856 player->switch_x = x;
14857 player->switch_y = y;
14859 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14861 if (element == EL_ROBOT_WHEEL)
14863 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14867 game.robot_wheel_active = TRUE;
14869 TEST_DrawLevelField(x, y);
14871 else if (element == EL_SP_TERMINAL)
14875 SCAN_PLAYFIELD(xx, yy)
14877 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14879 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14880 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14883 else if (IS_BELT_SWITCH(element))
14885 ToggleBeltSwitch(x, y);
14887 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14888 element == EL_SWITCHGATE_SWITCH_DOWN ||
14889 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14890 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14892 ToggleSwitchgateSwitch(x, y);
14894 else if (element == EL_LIGHT_SWITCH ||
14895 element == EL_LIGHT_SWITCH_ACTIVE)
14897 ToggleLightSwitch(x, y);
14899 else if (element == EL_TIMEGATE_SWITCH ||
14900 element == EL_DC_TIMEGATE_SWITCH)
14902 ActivateTimegateSwitch(x, y);
14904 else if (element == EL_BALLOON_SWITCH_LEFT ||
14905 element == EL_BALLOON_SWITCH_RIGHT ||
14906 element == EL_BALLOON_SWITCH_UP ||
14907 element == EL_BALLOON_SWITCH_DOWN ||
14908 element == EL_BALLOON_SWITCH_NONE ||
14909 element == EL_BALLOON_SWITCH_ANY)
14911 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14912 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14913 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14914 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14915 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14918 else if (element == EL_LAMP)
14920 Feld[x][y] = EL_LAMP_ACTIVE;
14921 local_player->lights_still_needed--;
14923 ResetGfxAnimation(x, y);
14924 TEST_DrawLevelField(x, y);
14926 else if (element == EL_TIME_ORB_FULL)
14928 Feld[x][y] = EL_TIME_ORB_EMPTY;
14930 if (level.time > 0 || level.use_time_orb_bug)
14932 TimeLeft += level.time_orb_time;
14935 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14937 DisplayGameControlValues();
14939 DrawGameValue_Time(TimeLeft);
14943 ResetGfxAnimation(x, y);
14944 TEST_DrawLevelField(x, y);
14946 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14947 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14951 game.ball_state = !game.ball_state;
14953 SCAN_PLAYFIELD(xx, yy)
14955 int e = Feld[xx][yy];
14957 if (game.ball_state)
14959 if (e == EL_EMC_MAGIC_BALL)
14960 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14961 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14962 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14966 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14967 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14968 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14969 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14974 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14975 player->index_bit, dig_side);
14977 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14978 player->index_bit, dig_side);
14980 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14981 player->index_bit, dig_side);
14987 if (!PLAYER_SWITCHING(player, x, y))
14989 player->is_switching = TRUE;
14990 player->switch_x = x;
14991 player->switch_y = y;
14993 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14994 player->index_bit, dig_side);
14995 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14996 player->index_bit, dig_side);
14998 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14999 player->index_bit, dig_side);
15000 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15001 player->index_bit, dig_side);
15004 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15005 player->index_bit, dig_side);
15006 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15007 player->index_bit, dig_side);
15009 return MP_NO_ACTION;
15012 player->push_delay = -1;
15014 if (is_player) /* function can also be called by EL_PENGUIN */
15016 if (Feld[x][y] != element) /* really digged/collected something */
15018 player->is_collecting = !player->is_digging;
15019 player->is_active = TRUE;
15026 static boolean DigFieldByCE(int x, int y, int digging_element)
15028 int element = Feld[x][y];
15030 if (!IS_FREE(x, y))
15032 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15033 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15036 /* no element can dig solid indestructible elements */
15037 if (IS_INDESTRUCTIBLE(element) &&
15038 !IS_DIGGABLE(element) &&
15039 !IS_COLLECTIBLE(element))
15042 if (AmoebaNr[x][y] &&
15043 (element == EL_AMOEBA_FULL ||
15044 element == EL_BD_AMOEBA ||
15045 element == EL_AMOEBA_GROWING))
15047 AmoebaCnt[AmoebaNr[x][y]]--;
15048 AmoebaCnt2[AmoebaNr[x][y]]--;
15051 if (IS_MOVING(x, y))
15052 RemoveMovingField(x, y);
15056 TEST_DrawLevelField(x, y);
15059 /* if digged element was about to explode, prevent the explosion */
15060 ExplodeField[x][y] = EX_TYPE_NONE;
15062 PlayLevelSoundAction(x, y, action);
15065 Store[x][y] = EL_EMPTY;
15068 /* this makes it possible to leave the removed element again */
15069 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15070 Store[x][y] = element;
15072 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15074 int move_leave_element = element_info[digging_element].move_leave_element;
15076 /* this makes it possible to leave the removed element again */
15077 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15078 element : move_leave_element);
15085 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15087 int jx = player->jx, jy = player->jy;
15088 int x = jx + dx, y = jy + dy;
15089 int snap_direction = (dx == -1 ? MV_LEFT :
15090 dx == +1 ? MV_RIGHT :
15092 dy == +1 ? MV_DOWN : MV_NONE);
15093 boolean can_continue_snapping = (level.continuous_snapping &&
15094 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15096 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15099 if (!player->active || !IN_LEV_FIELD(x, y))
15107 if (player->MovPos == 0)
15108 player->is_pushing = FALSE;
15110 player->is_snapping = FALSE;
15112 if (player->MovPos == 0)
15114 player->is_moving = FALSE;
15115 player->is_digging = FALSE;
15116 player->is_collecting = FALSE;
15122 #if USE_NEW_CONTINUOUS_SNAPPING
15123 /* prevent snapping with already pressed snap key when not allowed */
15124 if (player->is_snapping && !can_continue_snapping)
15127 if (player->is_snapping)
15131 player->MovDir = snap_direction;
15133 if (player->MovPos == 0)
15135 player->is_moving = FALSE;
15136 player->is_digging = FALSE;
15137 player->is_collecting = FALSE;
15140 player->is_dropping = FALSE;
15141 player->is_dropping_pressed = FALSE;
15142 player->drop_pressed_delay = 0;
15144 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15147 player->is_snapping = TRUE;
15148 player->is_active = TRUE;
15150 if (player->MovPos == 0)
15152 player->is_moving = FALSE;
15153 player->is_digging = FALSE;
15154 player->is_collecting = FALSE;
15157 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15158 TEST_DrawLevelField(player->last_jx, player->last_jy);
15160 TEST_DrawLevelField(x, y);
15165 static boolean DropElement(struct PlayerInfo *player)
15167 int old_element, new_element;
15168 int dropx = player->jx, dropy = player->jy;
15169 int drop_direction = player->MovDir;
15170 int drop_side = drop_direction;
15172 int drop_element = get_next_dropped_element(player);
15174 int drop_element = (player->inventory_size > 0 ?
15175 player->inventory_element[player->inventory_size - 1] :
15176 player->inventory_infinite_element != EL_UNDEFINED ?
15177 player->inventory_infinite_element :
15178 player->dynabombs_left > 0 ?
15179 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15183 player->is_dropping_pressed = TRUE;
15185 /* do not drop an element on top of another element; when holding drop key
15186 pressed without moving, dropped element must move away before the next
15187 element can be dropped (this is especially important if the next element
15188 is dynamite, which can be placed on background for historical reasons) */
15189 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15192 if (IS_THROWABLE(drop_element))
15194 dropx += GET_DX_FROM_DIR(drop_direction);
15195 dropy += GET_DY_FROM_DIR(drop_direction);
15197 if (!IN_LEV_FIELD(dropx, dropy))
15201 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15202 new_element = drop_element; /* default: no change when dropping */
15204 /* check if player is active, not moving and ready to drop */
15205 if (!player->active || player->MovPos || player->drop_delay > 0)
15208 /* check if player has anything that can be dropped */
15209 if (new_element == EL_UNDEFINED)
15212 /* check if drop key was pressed long enough for EM style dynamite */
15213 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15216 /* check if anything can be dropped at the current position */
15217 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15220 /* collected custom elements can only be dropped on empty fields */
15221 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15224 if (old_element != EL_EMPTY)
15225 Back[dropx][dropy] = old_element; /* store old element on this field */
15227 ResetGfxAnimation(dropx, dropy);
15228 ResetRandomAnimationValue(dropx, dropy);
15230 if (player->inventory_size > 0 ||
15231 player->inventory_infinite_element != EL_UNDEFINED)
15233 if (player->inventory_size > 0)
15235 player->inventory_size--;
15237 DrawGameDoorValues();
15239 if (new_element == EL_DYNAMITE)
15240 new_element = EL_DYNAMITE_ACTIVE;
15241 else if (new_element == EL_EM_DYNAMITE)
15242 new_element = EL_EM_DYNAMITE_ACTIVE;
15243 else if (new_element == EL_SP_DISK_RED)
15244 new_element = EL_SP_DISK_RED_ACTIVE;
15247 Feld[dropx][dropy] = new_element;
15249 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15250 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15251 el2img(Feld[dropx][dropy]), 0);
15253 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15255 /* needed if previous element just changed to "empty" in the last frame */
15256 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15258 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15259 player->index_bit, drop_side);
15260 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15262 player->index_bit, drop_side);
15264 TestIfElementTouchesCustomElement(dropx, dropy);
15266 else /* player is dropping a dyna bomb */
15268 player->dynabombs_left--;
15270 Feld[dropx][dropy] = new_element;
15272 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15273 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15274 el2img(Feld[dropx][dropy]), 0);
15276 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15279 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15280 InitField_WithBug1(dropx, dropy, FALSE);
15282 new_element = Feld[dropx][dropy]; /* element might have changed */
15284 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15285 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15287 int move_direction, nextx, nexty;
15289 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15290 MovDir[dropx][dropy] = drop_direction;
15292 move_direction = MovDir[dropx][dropy];
15293 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15294 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15296 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15298 #if USE_FIX_IMPACT_COLLISION
15299 /* do not cause impact style collision by dropping elements that can fall */
15300 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15302 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15306 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15307 player->is_dropping = TRUE;
15309 player->drop_pressed_delay = 0;
15310 player->is_dropping_pressed = FALSE;
15312 player->drop_x = dropx;
15313 player->drop_y = dropy;
15318 /* ------------------------------------------------------------------------- */
15319 /* game sound playing functions */
15320 /* ------------------------------------------------------------------------- */
15322 static int *loop_sound_frame = NULL;
15323 static int *loop_sound_volume = NULL;
15325 void InitPlayLevelSound()
15327 int num_sounds = getSoundListSize();
15329 checked_free(loop_sound_frame);
15330 checked_free(loop_sound_volume);
15332 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15333 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15336 static void PlayLevelSound(int x, int y, int nr)
15338 int sx = SCREENX(x), sy = SCREENY(y);
15339 int volume, stereo_position;
15340 int max_distance = 8;
15341 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15343 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15344 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15347 if (!IN_LEV_FIELD(x, y) ||
15348 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15349 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15352 volume = SOUND_MAX_VOLUME;
15354 if (!IN_SCR_FIELD(sx, sy))
15356 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15357 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15359 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15362 stereo_position = (SOUND_MAX_LEFT +
15363 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15364 (SCR_FIELDX + 2 * max_distance));
15366 if (IS_LOOP_SOUND(nr))
15368 /* This assures that quieter loop sounds do not overwrite louder ones,
15369 while restarting sound volume comparison with each new game frame. */
15371 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15374 loop_sound_volume[nr] = volume;
15375 loop_sound_frame[nr] = FrameCounter;
15378 PlaySoundExt(nr, volume, stereo_position, type);
15381 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15383 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15384 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15385 y < LEVELY(BY1) ? LEVELY(BY1) :
15386 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15390 static void PlayLevelSoundAction(int x, int y, int action)
15392 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15395 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15397 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15399 if (sound_effect != SND_UNDEFINED)
15400 PlayLevelSound(x, y, sound_effect);
15403 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15406 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15408 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15409 PlayLevelSound(x, y, sound_effect);
15412 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15414 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15416 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15417 PlayLevelSound(x, y, sound_effect);
15420 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15422 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15424 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15425 StopSound(sound_effect);
15428 static void PlayLevelMusic()
15430 if (levelset.music[level_nr] != MUS_UNDEFINED)
15431 PlayMusic(levelset.music[level_nr]); /* from config file */
15433 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15436 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15438 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15439 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15440 int x = xx - 1 - offset;
15441 int y = yy - 1 - offset;
15446 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15450 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15454 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15458 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15462 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15466 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15470 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15473 case SAMPLE_android_clone:
15474 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15477 case SAMPLE_android_move:
15478 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15481 case SAMPLE_spring:
15482 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15486 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15490 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15493 case SAMPLE_eater_eat:
15494 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15498 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15501 case SAMPLE_collect:
15502 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15505 case SAMPLE_diamond:
15506 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15509 case SAMPLE_squash:
15510 /* !!! CHECK THIS !!! */
15512 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15514 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15518 case SAMPLE_wonderfall:
15519 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15523 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15527 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15531 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15535 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15539 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15543 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15546 case SAMPLE_wonder:
15547 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15551 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15554 case SAMPLE_exit_open:
15555 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15558 case SAMPLE_exit_leave:
15559 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15562 case SAMPLE_dynamite:
15563 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15567 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15571 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15575 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15579 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15583 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15587 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15591 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15597 void ChangeTime(int value)
15599 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15603 /* EMC game engine uses value from time counter of RND game engine */
15604 level.native_em_level->lev->time = *time;
15606 DrawGameValue_Time(*time);
15609 void RaiseScore(int value)
15611 /* EMC game engine and RND game engine have separate score counters */
15612 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15613 &level.native_em_level->lev->score : &local_player->score);
15617 DrawGameValue_Score(*score);
15621 void RaiseScore(int value)
15623 local_player->score += value;
15626 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15628 DisplayGameControlValues();
15630 DrawGameValue_Score(local_player->score);
15634 void RaiseScoreElement(int element)
15639 case EL_BD_DIAMOND:
15640 case EL_EMERALD_YELLOW:
15641 case EL_EMERALD_RED:
15642 case EL_EMERALD_PURPLE:
15643 case EL_SP_INFOTRON:
15644 RaiseScore(level.score[SC_EMERALD]);
15647 RaiseScore(level.score[SC_DIAMOND]);
15650 RaiseScore(level.score[SC_CRYSTAL]);
15653 RaiseScore(level.score[SC_PEARL]);
15656 case EL_BD_BUTTERFLY:
15657 case EL_SP_ELECTRON:
15658 RaiseScore(level.score[SC_BUG]);
15661 case EL_BD_FIREFLY:
15662 case EL_SP_SNIKSNAK:
15663 RaiseScore(level.score[SC_SPACESHIP]);
15666 case EL_DARK_YAMYAM:
15667 RaiseScore(level.score[SC_YAMYAM]);
15670 RaiseScore(level.score[SC_ROBOT]);
15673 RaiseScore(level.score[SC_PACMAN]);
15676 RaiseScore(level.score[SC_NUT]);
15679 case EL_EM_DYNAMITE:
15680 case EL_SP_DISK_RED:
15681 case EL_DYNABOMB_INCREASE_NUMBER:
15682 case EL_DYNABOMB_INCREASE_SIZE:
15683 case EL_DYNABOMB_INCREASE_POWER:
15684 RaiseScore(level.score[SC_DYNAMITE]);
15686 case EL_SHIELD_NORMAL:
15687 case EL_SHIELD_DEADLY:
15688 RaiseScore(level.score[SC_SHIELD]);
15690 case EL_EXTRA_TIME:
15691 RaiseScore(level.extra_time_score);
15705 case EL_DC_KEY_WHITE:
15706 RaiseScore(level.score[SC_KEY]);
15709 RaiseScore(element_info[element].collect_score);
15714 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15716 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15718 #if defined(NETWORK_AVALIABLE)
15719 if (options.network)
15720 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15729 FadeSkipNextFadeIn();
15731 fading = fading_none;
15735 OpenDoor(DOOR_CLOSE_1);
15738 game_status = GAME_MODE_MAIN;
15741 DrawAndFadeInMainMenu(REDRAW_FIELD);
15749 FadeOut(REDRAW_FIELD);
15752 game_status = GAME_MODE_MAIN;
15754 DrawAndFadeInMainMenu(REDRAW_FIELD);
15758 else /* continue playing the game */
15760 if (tape.playing && tape.deactivate_display)
15761 TapeDeactivateDisplayOff(TRUE);
15763 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15765 if (tape.playing && tape.deactivate_display)
15766 TapeDeactivateDisplayOn();
15770 void RequestQuitGame(boolean ask_if_really_quit)
15772 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15773 boolean skip_request = AllPlayersGone || quick_quit;
15775 RequestQuitGameExt(skip_request, quick_quit,
15776 "Do you really want to quit the game ?");
15780 /* ------------------------------------------------------------------------- */
15781 /* random generator functions */
15782 /* ------------------------------------------------------------------------- */
15784 unsigned int InitEngineRandom_RND(long seed)
15786 game.num_random_calls = 0;
15789 unsigned int rnd_seed = InitEngineRandom(seed);
15791 printf("::: START RND: %d\n", rnd_seed);
15796 return InitEngineRandom(seed);
15802 unsigned int RND(int max)
15806 game.num_random_calls++;
15808 return GetEngineRandom(max);
15815 /* ------------------------------------------------------------------------- */
15816 /* game engine snapshot handling functions */
15817 /* ------------------------------------------------------------------------- */
15819 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15821 struct EngineSnapshotInfo
15823 /* runtime values for custom element collect score */
15824 int collect_score[NUM_CUSTOM_ELEMENTS];
15826 /* runtime values for group element choice position */
15827 int choice_pos[NUM_GROUP_ELEMENTS];
15829 /* runtime values for belt position animations */
15830 int belt_graphic[4 * NUM_BELT_PARTS];
15831 int belt_anim_mode[4 * NUM_BELT_PARTS];
15834 struct EngineSnapshotNodeInfo
15841 static struct EngineSnapshotInfo engine_snapshot_rnd;
15842 static ListNode *engine_snapshot_list = NULL;
15843 static char *snapshot_level_identifier = NULL;
15844 static int snapshot_level_nr = -1;
15846 void FreeEngineSnapshot()
15848 while (engine_snapshot_list != NULL)
15849 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15852 setString(&snapshot_level_identifier, NULL);
15853 snapshot_level_nr = -1;
15856 static void SaveEngineSnapshotValues_RND()
15858 static int belt_base_active_element[4] =
15860 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15861 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15862 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15863 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15867 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15869 int element = EL_CUSTOM_START + i;
15871 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15874 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15876 int element = EL_GROUP_START + i;
15878 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15881 for (i = 0; i < 4; i++)
15883 for (j = 0; j < NUM_BELT_PARTS; j++)
15885 int element = belt_base_active_element[i] + j;
15886 int graphic = el2img(element);
15887 int anim_mode = graphic_info[graphic].anim_mode;
15889 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15890 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15895 static void LoadEngineSnapshotValues_RND()
15897 unsigned long num_random_calls = game.num_random_calls;
15900 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15902 int element = EL_CUSTOM_START + i;
15904 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15907 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15909 int element = EL_GROUP_START + i;
15911 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15914 for (i = 0; i < 4; i++)
15916 for (j = 0; j < NUM_BELT_PARTS; j++)
15918 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15919 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15921 graphic_info[graphic].anim_mode = anim_mode;
15925 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15927 InitRND(tape.random_seed);
15928 for (i = 0; i < num_random_calls; i++)
15932 if (game.num_random_calls != num_random_calls)
15934 Error(ERR_INFO, "number of random calls out of sync");
15935 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15936 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15937 Error(ERR_EXIT, "this should not happen -- please debug");
15941 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15943 struct EngineSnapshotNodeInfo *bi =
15944 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15946 bi->buffer_orig = buffer;
15947 bi->buffer_copy = checked_malloc(size);
15950 memcpy(bi->buffer_copy, buffer, size);
15952 addNodeToList(&engine_snapshot_list, NULL, bi);
15955 void SaveEngineSnapshot()
15957 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15959 if (level_editor_test_game) /* do not save snapshots from editor */
15962 /* copy some special values to a structure better suited for the snapshot */
15964 SaveEngineSnapshotValues_RND();
15965 SaveEngineSnapshotValues_EM();
15967 /* save values stored in special snapshot structure */
15969 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15970 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15972 /* save further RND engine values */
15974 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15975 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15976 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15978 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15979 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15980 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15981 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15983 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15984 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15985 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15986 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15987 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15989 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15990 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15991 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15993 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15995 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15997 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15998 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16000 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16001 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16002 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16003 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16004 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16005 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16006 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16007 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16008 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16009 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16010 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16011 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16012 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16013 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16014 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16015 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16016 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16017 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16019 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16020 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16022 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16023 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16024 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16026 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16027 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16029 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16030 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16031 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16032 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16033 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16035 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16036 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16038 /* save level identification information */
16040 setString(&snapshot_level_identifier, leveldir_current->identifier);
16041 snapshot_level_nr = level_nr;
16044 ListNode *node = engine_snapshot_list;
16047 while (node != NULL)
16049 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16054 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16058 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16060 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16063 void LoadEngineSnapshot()
16065 ListNode *node = engine_snapshot_list;
16067 if (engine_snapshot_list == NULL)
16070 while (node != NULL)
16072 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16077 /* restore special values from snapshot structure */
16079 LoadEngineSnapshotValues_RND();
16080 LoadEngineSnapshotValues_EM();
16083 boolean CheckEngineSnapshot()
16085 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16086 snapshot_level_nr == level_nr);
16090 /* ---------- new game button stuff ---------------------------------------- */
16092 /* graphic position values for game buttons */
16093 #define GAME_BUTTON_XSIZE 30
16094 #define GAME_BUTTON_YSIZE 30
16095 #define GAME_BUTTON_XPOS 5
16096 #define GAME_BUTTON_YPOS 215
16097 #define SOUND_BUTTON_XPOS 5
16098 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16100 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16101 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16102 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16103 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16104 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16105 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16113 } gamebutton_info[NUM_GAME_BUTTONS] =
16117 &game.button.stop.x, &game.button.stop.y,
16118 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16123 &game.button.pause.x, &game.button.pause.y,
16124 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16125 GAME_CTRL_ID_PAUSE,
16129 &game.button.play.x, &game.button.play.y,
16130 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16135 &game.button.sound_music.x, &game.button.sound_music.y,
16136 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16137 SOUND_CTRL_ID_MUSIC,
16138 "background music on/off"
16141 &game.button.sound_loops.x, &game.button.sound_loops.y,
16142 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16143 SOUND_CTRL_ID_LOOPS,
16144 "sound loops on/off"
16147 &game.button.sound_simple.x,&game.button.sound_simple.y,
16148 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16149 SOUND_CTRL_ID_SIMPLE,
16150 "normal sounds on/off"
16154 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16159 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16160 GAME_CTRL_ID_PAUSE,
16164 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16169 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16170 SOUND_CTRL_ID_MUSIC,
16171 "background music on/off"
16174 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16175 SOUND_CTRL_ID_LOOPS,
16176 "sound loops on/off"
16179 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16180 SOUND_CTRL_ID_SIMPLE,
16181 "normal sounds on/off"
16186 void CreateGameButtons()
16190 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16192 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16193 struct GadgetInfo *gi;
16196 unsigned long event_mask;
16198 int gd_xoffset, gd_yoffset;
16199 int gd_x1, gd_x2, gd_y1, gd_y2;
16202 x = DX + *gamebutton_info[i].x;
16203 y = DY + *gamebutton_info[i].y;
16204 gd_xoffset = gamebutton_info[i].gd_x;
16205 gd_yoffset = gamebutton_info[i].gd_y;
16206 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16207 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16209 if (id == GAME_CTRL_ID_STOP ||
16210 id == GAME_CTRL_ID_PAUSE ||
16211 id == GAME_CTRL_ID_PLAY)
16213 button_type = GD_TYPE_NORMAL_BUTTON;
16215 event_mask = GD_EVENT_RELEASED;
16216 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16217 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16221 button_type = GD_TYPE_CHECK_BUTTON;
16223 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16224 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16225 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16226 event_mask = GD_EVENT_PRESSED;
16227 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16228 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16231 gi = CreateGadget(GDI_CUSTOM_ID, id,
16232 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16237 GDI_X, DX + gd_xoffset,
16238 GDI_Y, DY + gd_yoffset,
16240 GDI_WIDTH, GAME_BUTTON_XSIZE,
16241 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16242 GDI_TYPE, button_type,
16243 GDI_STATE, GD_BUTTON_UNPRESSED,
16244 GDI_CHECKED, checked,
16245 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16246 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16247 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16248 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16249 GDI_DIRECT_DRAW, FALSE,
16250 GDI_EVENT_MASK, event_mask,
16251 GDI_CALLBACK_ACTION, HandleGameButtons,
16255 Error(ERR_EXIT, "cannot create gadget");
16257 game_gadget[id] = gi;
16261 void FreeGameButtons()
16265 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16266 FreeGadget(game_gadget[i]);
16269 static void MapGameButtons()
16273 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16274 MapGadget(game_gadget[i]);
16277 void UnmapGameButtons()
16281 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16282 UnmapGadget(game_gadget[i]);
16285 void RedrawGameButtons()
16289 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16290 RedrawGadget(game_gadget[i]);
16293 static void HandleGameButtons(struct GadgetInfo *gi)
16295 int id = gi->custom_id;
16297 if (game_status != GAME_MODE_PLAYING)
16302 case GAME_CTRL_ID_STOP:
16306 RequestQuitGame(TRUE);
16309 case GAME_CTRL_ID_PAUSE:
16310 if (options.network)
16312 #if defined(NETWORK_AVALIABLE)
16314 SendToServer_ContinuePlaying();
16316 SendToServer_PausePlaying();
16320 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16323 case GAME_CTRL_ID_PLAY:
16326 #if defined(NETWORK_AVALIABLE)
16327 if (options.network)
16328 SendToServer_ContinuePlaying();
16332 tape.pausing = FALSE;
16333 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16338 case SOUND_CTRL_ID_MUSIC:
16339 if (setup.sound_music)
16341 setup.sound_music = FALSE;
16344 else if (audio.music_available)
16346 setup.sound = setup.sound_music = TRUE;
16348 SetAudioMode(setup.sound);
16354 case SOUND_CTRL_ID_LOOPS:
16355 if (setup.sound_loops)
16356 setup.sound_loops = FALSE;
16357 else if (audio.loops_available)
16359 setup.sound = setup.sound_loops = TRUE;
16360 SetAudioMode(setup.sound);
16364 case SOUND_CTRL_ID_SIMPLE:
16365 if (setup.sound_simple)
16366 setup.sound_simple = FALSE;
16367 else if (audio.sound_available)
16369 setup.sound = setup.sound_simple = TRUE;
16370 SetAudioMode(setup.sound);