1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
30 #define USE_NEW_SP_SLIPPERY (USE_NEW_STUFF * 1)
31 #define USE_NEW_CUSTOM_VALUE (USE_NEW_STUFF * 1)
32 #define USE_NEW_PLAYER_ANIM (USE_NEW_STUFF * 1)
33 #define USE_NEW_ALL_SLIPPERY (USE_NEW_STUFF * 1)
34 #define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
35 #define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
36 #define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME (USE_NEW_STUFF * 1)
39 #define USE_FIXED_DONT_RUN_INTO (USE_NEW_STUFF * 1)
40 #define USE_NEW_SPRING_BUMPER (USE_NEW_STUFF * 1)
41 #define USE_STOP_CHANGED_ELEMENTS (USE_NEW_STUFF * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
46 #define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
59 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF * 1)
60 #define USE_FIX_IMPACT_COLLISION (USE_NEW_STUFF * 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER (USE_NEW_STUFF * 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF * 1)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
78 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y) \
80 GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
85 DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
87 DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START 0
110 #define EX_TYPE_NONE 0
111 #define EX_TYPE_NORMAL (1 << 0)
112 #define EX_TYPE_CENTER (1 << 1)
113 #define EX_TYPE_BORDER (1 << 2)
114 #define EX_TYPE_CROSS (1 << 3)
115 #define EX_TYPE_DYNA (1 << 4)
116 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
118 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
119 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
123 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE (PANEL_XPOS(game.panel.score))
135 #define YY_SCORE (PANEL_YPOS(game.panel.score))
136 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
137 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME (PANEL_XPOS(game.panel.time))
139 #define YY_TIME (PANEL_YPOS(game.panel.time))
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1 (DX + XX_LEVEL1)
143 #define DX_LEVEL2 (DX + XX_LEVEL2)
144 #define DX_LEVEL (DX + XX_LEVEL)
145 #define DY_LEVEL (DY + YY_LEVEL)
146 #define DX_EMERALDS (DX + XX_EMERALDS)
147 #define DY_EMERALDS (DY + YY_EMERALDS)
148 #define DX_DYNAMITE (DX + XX_DYNAMITE)
149 #define DY_DYNAMITE (DY + YY_DYNAMITE)
150 #define DX_KEYS (DX + XX_KEYS)
151 #define DY_KEYS (DY + YY_KEYS)
152 #define DX_SCORE (DX + XX_SCORE)
153 #define DY_SCORE (DY + YY_SCORE)
154 #define DX_TIME1 (DX + XX_TIME1)
155 #define DX_TIME2 (DX + XX_TIME2)
156 #define DX_TIME (DX + XX_TIME)
157 #define DY_TIME (DY + YY_TIME)
160 /* game panel display and control definitions */
162 #define GAME_PANEL_LEVEL_NUMBER 0
163 #define GAME_PANEL_GEMS 1
164 #define GAME_PANEL_INVENTORY_COUNT 2
165 #define GAME_PANEL_INVENTORY_FIRST_1 3
166 #define GAME_PANEL_INVENTORY_FIRST_2 4
167 #define GAME_PANEL_INVENTORY_FIRST_3 5
168 #define GAME_PANEL_INVENTORY_FIRST_4 6
169 #define GAME_PANEL_INVENTORY_FIRST_5 7
170 #define GAME_PANEL_INVENTORY_FIRST_6 8
171 #define GAME_PANEL_INVENTORY_FIRST_7 9
172 #define GAME_PANEL_INVENTORY_FIRST_8 10
173 #define GAME_PANEL_INVENTORY_LAST_1 11
174 #define GAME_PANEL_INVENTORY_LAST_2 12
175 #define GAME_PANEL_INVENTORY_LAST_3 13
176 #define GAME_PANEL_INVENTORY_LAST_4 14
177 #define GAME_PANEL_INVENTORY_LAST_5 15
178 #define GAME_PANEL_INVENTORY_LAST_6 16
179 #define GAME_PANEL_INVENTORY_LAST_7 17
180 #define GAME_PANEL_INVENTORY_LAST_8 18
181 #define GAME_PANEL_KEY_1 19
182 #define GAME_PANEL_KEY_2 20
183 #define GAME_PANEL_KEY_3 21
184 #define GAME_PANEL_KEY_4 22
185 #define GAME_PANEL_KEY_5 23
186 #define GAME_PANEL_KEY_6 24
187 #define GAME_PANEL_KEY_7 25
188 #define GAME_PANEL_KEY_8 26
189 #define GAME_PANEL_KEY_WHITE 27
190 #define GAME_PANEL_KEY_WHITE_COUNT 28
191 #define GAME_PANEL_SCORE 29
192 #define GAME_PANEL_HIGHSCORE 30
193 #define GAME_PANEL_TIME 31
194 #define GAME_PANEL_TIME_HH 32
195 #define GAME_PANEL_TIME_MM 33
196 #define GAME_PANEL_TIME_SS 34
197 #define GAME_PANEL_SHIELD_NORMAL 35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
199 #define GAME_PANEL_SHIELD_DEADLY 37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
201 #define GAME_PANEL_EXIT 39
202 #define GAME_PANEL_EMC_MAGIC_BALL 40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
204 #define GAME_PANEL_LIGHT_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
206 #define GAME_PANEL_TIMEGATE_SWITCH 44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
208 #define GAME_PANEL_SWITCHGATE_SWITCH 46
209 #define GAME_PANEL_EMC_LENSES 47
210 #define GAME_PANEL_EMC_LENSES_TIME 48
211 #define GAME_PANEL_EMC_MAGNIFIER 49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
213 #define GAME_PANEL_BALLOON_SWITCH 51
214 #define GAME_PANEL_DYNABOMB_NUMBER 52
215 #define GAME_PANEL_DYNABOMB_SIZE 53
216 #define GAME_PANEL_DYNABOMB_POWER 54
217 #define GAME_PANEL_PENGUINS 55
218 #define GAME_PANEL_SOKOBAN_OBJECTS 56
219 #define GAME_PANEL_SOKOBAN_FIELDS 57
220 #define GAME_PANEL_ROBOT_WHEEL 58
221 #define GAME_PANEL_CONVEYOR_BELT_1 59
222 #define GAME_PANEL_CONVEYOR_BELT_2 60
223 #define GAME_PANEL_CONVEYOR_BELT_3 61
224 #define GAME_PANEL_CONVEYOR_BELT_4 62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
229 #define GAME_PANEL_MAGIC_WALL 67
230 #define GAME_PANEL_MAGIC_WALL_TIME 68
231 #define GAME_PANEL_GRAVITY_STATE 69
232 #define GAME_PANEL_GRAPHIC_1 70
233 #define GAME_PANEL_GRAPHIC_2 71
234 #define GAME_PANEL_GRAPHIC_3 72
235 #define GAME_PANEL_GRAPHIC_4 73
236 #define GAME_PANEL_GRAPHIC_5 74
237 #define GAME_PANEL_GRAPHIC_6 75
238 #define GAME_PANEL_GRAPHIC_7 76
239 #define GAME_PANEL_GRAPHIC_8 77
240 #define GAME_PANEL_ELEMENT_1 78
241 #define GAME_PANEL_ELEMENT_2 79
242 #define GAME_PANEL_ELEMENT_3 80
243 #define GAME_PANEL_ELEMENT_4 81
244 #define GAME_PANEL_ELEMENT_5 82
245 #define GAME_PANEL_ELEMENT_6 83
246 #define GAME_PANEL_ELEMENT_7 84
247 #define GAME_PANEL_ELEMENT_8 85
248 #define GAME_PANEL_ELEMENT_COUNT_1 86
249 #define GAME_PANEL_ELEMENT_COUNT_2 87
250 #define GAME_PANEL_ELEMENT_COUNT_3 88
251 #define GAME_PANEL_ELEMENT_COUNT_4 89
252 #define GAME_PANEL_ELEMENT_COUNT_5 90
253 #define GAME_PANEL_ELEMENT_COUNT_6 91
254 #define GAME_PANEL_ELEMENT_COUNT_7 92
255 #define GAME_PANEL_ELEMENT_COUNT_8 93
256 #define GAME_PANEL_CE_SCORE_1 94
257 #define GAME_PANEL_CE_SCORE_2 95
258 #define GAME_PANEL_CE_SCORE_3 96
259 #define GAME_PANEL_CE_SCORE_4 97
260 #define GAME_PANEL_CE_SCORE_5 98
261 #define GAME_PANEL_CE_SCORE_6 99
262 #define GAME_PANEL_CE_SCORE_7 100
263 #define GAME_PANEL_CE_SCORE_8 101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
272 #define GAME_PANEL_PLAYER_NAME 110
273 #define GAME_PANEL_LEVEL_NAME 111
274 #define GAME_PANEL_LEVEL_AUTHOR 112
276 #define NUM_GAME_PANEL_CONTROLS 113
278 struct GamePanelOrderInfo
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286 struct GamePanelControlInfo
290 struct TextPosInfo *pos;
293 int value, last_value;
294 int frame, last_frame;
299 static struct GamePanelControlInfo game_panel_controls[] =
302 GAME_PANEL_LEVEL_NUMBER,
303 &game.panel.level_number,
312 GAME_PANEL_INVENTORY_COUNT,
313 &game.panel.inventory_count,
317 GAME_PANEL_INVENTORY_FIRST_1,
318 &game.panel.inventory_first[0],
322 GAME_PANEL_INVENTORY_FIRST_2,
323 &game.panel.inventory_first[1],
327 GAME_PANEL_INVENTORY_FIRST_3,
328 &game.panel.inventory_first[2],
332 GAME_PANEL_INVENTORY_FIRST_4,
333 &game.panel.inventory_first[3],
337 GAME_PANEL_INVENTORY_FIRST_5,
338 &game.panel.inventory_first[4],
342 GAME_PANEL_INVENTORY_FIRST_6,
343 &game.panel.inventory_first[5],
347 GAME_PANEL_INVENTORY_FIRST_7,
348 &game.panel.inventory_first[6],
352 GAME_PANEL_INVENTORY_FIRST_8,
353 &game.panel.inventory_first[7],
357 GAME_PANEL_INVENTORY_LAST_1,
358 &game.panel.inventory_last[0],
362 GAME_PANEL_INVENTORY_LAST_2,
363 &game.panel.inventory_last[1],
367 GAME_PANEL_INVENTORY_LAST_3,
368 &game.panel.inventory_last[2],
372 GAME_PANEL_INVENTORY_LAST_4,
373 &game.panel.inventory_last[3],
377 GAME_PANEL_INVENTORY_LAST_5,
378 &game.panel.inventory_last[4],
382 GAME_PANEL_INVENTORY_LAST_6,
383 &game.panel.inventory_last[5],
387 GAME_PANEL_INVENTORY_LAST_7,
388 &game.panel.inventory_last[6],
392 GAME_PANEL_INVENTORY_LAST_8,
393 &game.panel.inventory_last[7],
437 GAME_PANEL_KEY_WHITE,
438 &game.panel.key_white,
442 GAME_PANEL_KEY_WHITE_COUNT,
443 &game.panel.key_white_count,
452 GAME_PANEL_HIGHSCORE,
453 &game.panel.highscore,
477 GAME_PANEL_SHIELD_NORMAL,
478 &game.panel.shield_normal,
482 GAME_PANEL_SHIELD_NORMAL_TIME,
483 &game.panel.shield_normal_time,
487 GAME_PANEL_SHIELD_DEADLY,
488 &game.panel.shield_deadly,
492 GAME_PANEL_SHIELD_DEADLY_TIME,
493 &game.panel.shield_deadly_time,
502 GAME_PANEL_EMC_MAGIC_BALL,
503 &game.panel.emc_magic_ball,
507 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508 &game.panel.emc_magic_ball_switch,
512 GAME_PANEL_LIGHT_SWITCH,
513 &game.panel.light_switch,
517 GAME_PANEL_LIGHT_SWITCH_TIME,
518 &game.panel.light_switch_time,
522 GAME_PANEL_TIMEGATE_SWITCH,
523 &game.panel.timegate_switch,
527 GAME_PANEL_TIMEGATE_SWITCH_TIME,
528 &game.panel.timegate_switch_time,
532 GAME_PANEL_SWITCHGATE_SWITCH,
533 &game.panel.switchgate_switch,
537 GAME_PANEL_EMC_LENSES,
538 &game.panel.emc_lenses,
542 GAME_PANEL_EMC_LENSES_TIME,
543 &game.panel.emc_lenses_time,
547 GAME_PANEL_EMC_MAGNIFIER,
548 &game.panel.emc_magnifier,
552 GAME_PANEL_EMC_MAGNIFIER_TIME,
553 &game.panel.emc_magnifier_time,
557 GAME_PANEL_BALLOON_SWITCH,
558 &game.panel.balloon_switch,
562 GAME_PANEL_DYNABOMB_NUMBER,
563 &game.panel.dynabomb_number,
567 GAME_PANEL_DYNABOMB_SIZE,
568 &game.panel.dynabomb_size,
572 GAME_PANEL_DYNABOMB_POWER,
573 &game.panel.dynabomb_power,
578 &game.panel.penguins,
582 GAME_PANEL_SOKOBAN_OBJECTS,
583 &game.panel.sokoban_objects,
587 GAME_PANEL_SOKOBAN_FIELDS,
588 &game.panel.sokoban_fields,
592 GAME_PANEL_ROBOT_WHEEL,
593 &game.panel.robot_wheel,
597 GAME_PANEL_CONVEYOR_BELT_1,
598 &game.panel.conveyor_belt[0],
602 GAME_PANEL_CONVEYOR_BELT_2,
603 &game.panel.conveyor_belt[1],
607 GAME_PANEL_CONVEYOR_BELT_3,
608 &game.panel.conveyor_belt[2],
612 GAME_PANEL_CONVEYOR_BELT_4,
613 &game.panel.conveyor_belt[3],
617 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618 &game.panel.conveyor_belt_switch[0],
622 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623 &game.panel.conveyor_belt_switch[1],
627 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628 &game.panel.conveyor_belt_switch[2],
632 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633 &game.panel.conveyor_belt_switch[3],
637 GAME_PANEL_MAGIC_WALL,
638 &game.panel.magic_wall,
642 GAME_PANEL_MAGIC_WALL_TIME,
643 &game.panel.magic_wall_time,
647 GAME_PANEL_GRAVITY_STATE,
648 &game.panel.gravity_state,
652 GAME_PANEL_GRAPHIC_1,
653 &game.panel.graphic[0],
657 GAME_PANEL_GRAPHIC_2,
658 &game.panel.graphic[1],
662 GAME_PANEL_GRAPHIC_3,
663 &game.panel.graphic[2],
667 GAME_PANEL_GRAPHIC_4,
668 &game.panel.graphic[3],
672 GAME_PANEL_GRAPHIC_5,
673 &game.panel.graphic[4],
677 GAME_PANEL_GRAPHIC_6,
678 &game.panel.graphic[5],
682 GAME_PANEL_GRAPHIC_7,
683 &game.panel.graphic[6],
687 GAME_PANEL_GRAPHIC_8,
688 &game.panel.graphic[7],
692 GAME_PANEL_ELEMENT_1,
693 &game.panel.element[0],
697 GAME_PANEL_ELEMENT_2,
698 &game.panel.element[1],
702 GAME_PANEL_ELEMENT_3,
703 &game.panel.element[2],
707 GAME_PANEL_ELEMENT_4,
708 &game.panel.element[3],
712 GAME_PANEL_ELEMENT_5,
713 &game.panel.element[4],
717 GAME_PANEL_ELEMENT_6,
718 &game.panel.element[5],
722 GAME_PANEL_ELEMENT_7,
723 &game.panel.element[6],
727 GAME_PANEL_ELEMENT_8,
728 &game.panel.element[7],
732 GAME_PANEL_ELEMENT_COUNT_1,
733 &game.panel.element_count[0],
737 GAME_PANEL_ELEMENT_COUNT_2,
738 &game.panel.element_count[1],
742 GAME_PANEL_ELEMENT_COUNT_3,
743 &game.panel.element_count[2],
747 GAME_PANEL_ELEMENT_COUNT_4,
748 &game.panel.element_count[3],
752 GAME_PANEL_ELEMENT_COUNT_5,
753 &game.panel.element_count[4],
757 GAME_PANEL_ELEMENT_COUNT_6,
758 &game.panel.element_count[5],
762 GAME_PANEL_ELEMENT_COUNT_7,
763 &game.panel.element_count[6],
767 GAME_PANEL_ELEMENT_COUNT_8,
768 &game.panel.element_count[7],
772 GAME_PANEL_CE_SCORE_1,
773 &game.panel.ce_score[0],
777 GAME_PANEL_CE_SCORE_2,
778 &game.panel.ce_score[1],
782 GAME_PANEL_CE_SCORE_3,
783 &game.panel.ce_score[2],
787 GAME_PANEL_CE_SCORE_4,
788 &game.panel.ce_score[3],
792 GAME_PANEL_CE_SCORE_5,
793 &game.panel.ce_score[4],
797 GAME_PANEL_CE_SCORE_6,
798 &game.panel.ce_score[5],
802 GAME_PANEL_CE_SCORE_7,
803 &game.panel.ce_score[6],
807 GAME_PANEL_CE_SCORE_8,
808 &game.panel.ce_score[7],
812 GAME_PANEL_CE_SCORE_1_ELEMENT,
813 &game.panel.ce_score_element[0],
817 GAME_PANEL_CE_SCORE_2_ELEMENT,
818 &game.panel.ce_score_element[1],
822 GAME_PANEL_CE_SCORE_3_ELEMENT,
823 &game.panel.ce_score_element[2],
827 GAME_PANEL_CE_SCORE_4_ELEMENT,
828 &game.panel.ce_score_element[3],
832 GAME_PANEL_CE_SCORE_5_ELEMENT,
833 &game.panel.ce_score_element[4],
837 GAME_PANEL_CE_SCORE_6_ELEMENT,
838 &game.panel.ce_score_element[5],
842 GAME_PANEL_CE_SCORE_7_ELEMENT,
843 &game.panel.ce_score_element[6],
847 GAME_PANEL_CE_SCORE_8_ELEMENT,
848 &game.panel.ce_score_element[7],
852 GAME_PANEL_PLAYER_NAME,
853 &game.panel.player_name,
857 GAME_PANEL_LEVEL_NAME,
858 &game.panel.level_name,
862 GAME_PANEL_LEVEL_AUTHOR,
863 &game.panel.level_author,
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING 3
878 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION 2
880 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF -1
884 #define INITIAL_MOVE_DELAY_ON 0
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED 32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED 4
890 #define MOVE_DELAY_MAX_SPEED 1
892 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
895 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN (1)
901 #define MOVE_STEPSIZE_MAX (TILEX)
903 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
906 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
908 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
909 RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
911 RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
913 RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
915 (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
917 RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
920 RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
922 RND((c)->delay_random))
925 #define GET_VALID_RUNTIME_ELEMENT(e) \
926 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
928 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
929 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
930 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
931 (be) + (e) - EL_SELF)
933 #define GET_PLAYER_FROM_BITS(p) \
934 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
937 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
938 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
939 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
940 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
941 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
942 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
943 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
944 RESOLVED_REFERENCE_ELEMENT(be, e) : \
947 #define CAN_GROW_INTO(e) \
948 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
951 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
955 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
956 (CAN_MOVE_INTO_ACID(e) && \
957 Feld[x][y] == EL_ACID) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
969 (CAN_MOVE_INTO_ACID(e) && \
970 Feld[x][y] == EL_ACID) || \
971 (DONT_COLLIDE_WITH(e) && \
973 !PLAYER_ENEMY_PROTECTED(x, y))))
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
978 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
984 #define ANDROID_CAN_CLONE_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
989 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1000 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005 Feld[x][y] == EL_EM_EXIT_OPEN || \
1006 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008 IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1013 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1019 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1020 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1022 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1024 #define CE_ENTER_FIELD_COND(e, x, y) \
1025 (!IS_PLAYER(x, y) && \
1026 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1029 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1034 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP 0
1041 #define GAME_CTRL_ID_PAUSE 1
1042 #define GAME_CTRL_ID_PLAY 2
1043 #define SOUND_CTRL_ID_MUSIC 3
1044 #define SOUND_CTRL_ID_LOOPS 4
1045 #define SOUND_CTRL_ID_SIMPLE 5
1047 #define NUM_GAME_BUTTONS 6
1050 /* forward declaration for internal use */
1052 static void CreateField(int, int, int);
1054 static void ResetGfxAnimation(int, int);
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev) \
1089 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1091 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1093 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev) \
1099 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1101 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1103 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1149 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1151 if (recursion_loop_detected) \
1154 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1156 recursion_loop_detected = TRUE; \
1157 recursion_loop_element = (e); \
1160 recursion_loop_depth++; \
1163 #define RECURSION_LOOP_DETECTION_END() \
1165 recursion_loop_depth--; \
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1173 /* ------------------------------------------------------------------------- */
1174 /* definition of elements that automatically change to other elements after */
1175 /* a specified time, eventually calling a function when changing */
1176 /* ------------------------------------------------------------------------- */
1178 /* forward declaration for changer functions */
1179 static void InitBuggyBase(int, int);
1180 static void WarnBuggyBase(int, int);
1182 static void InitTrap(int, int);
1183 static void ActivateTrap(int, int);
1184 static void ChangeActiveTrap(int, int);
1186 static void InitRobotWheel(int, int);
1187 static void RunRobotWheel(int, int);
1188 static void StopRobotWheel(int, int);
1190 static void InitTimegateWheel(int, int);
1191 static void RunTimegateWheel(int, int);
1193 static void InitMagicBallDelay(int, int);
1194 static void ActivateMagicBall(int, int);
1196 struct ChangingElementInfo
1201 void (*pre_change_function)(int x, int y);
1202 void (*change_function)(int x, int y);
1203 void (*post_change_function)(int x, int y);
1206 static struct ChangingElementInfo change_delay_list[] =
1241 EL_STEEL_EXIT_OPENING,
1249 EL_STEEL_EXIT_CLOSING,
1250 EL_STEEL_EXIT_CLOSED,
1277 EL_EM_STEEL_EXIT_OPENING,
1278 EL_EM_STEEL_EXIT_OPEN,
1285 EL_EM_STEEL_EXIT_CLOSING,
1289 EL_EM_STEEL_EXIT_CLOSED,
1313 EL_SWITCHGATE_OPENING,
1321 EL_SWITCHGATE_CLOSING,
1322 EL_SWITCHGATE_CLOSED,
1329 EL_TIMEGATE_OPENING,
1337 EL_TIMEGATE_CLOSING,
1346 EL_ACID_SPLASH_LEFT,
1354 EL_ACID_SPLASH_RIGHT,
1363 EL_SP_BUGGY_BASE_ACTIVATING,
1370 EL_SP_BUGGY_BASE_ACTIVATING,
1371 EL_SP_BUGGY_BASE_ACTIVE,
1378 EL_SP_BUGGY_BASE_ACTIVE,
1402 EL_ROBOT_WHEEL_ACTIVE,
1410 EL_TIMEGATE_SWITCH_ACTIVE,
1418 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1419 EL_DC_TIMEGATE_SWITCH,
1426 EL_EMC_MAGIC_BALL_ACTIVE,
1427 EL_EMC_MAGIC_BALL_ACTIVE,
1434 EL_EMC_SPRING_BUMPER_ACTIVE,
1435 EL_EMC_SPRING_BUMPER,
1442 EL_DIAGONAL_SHRINKING,
1450 EL_DIAGONAL_GROWING,
1471 int push_delay_fixed, push_delay_random;
1475 { EL_SPRING, 0, 0 },
1476 { EL_BALLOON, 0, 0 },
1478 { EL_SOKOBAN_OBJECT, 2, 0 },
1479 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1480 { EL_SATELLITE, 2, 0 },
1481 { EL_SP_DISK_YELLOW, 2, 0 },
1483 { EL_UNDEFINED, 0, 0 },
1491 move_stepsize_list[] =
1493 { EL_AMOEBA_DROP, 2 },
1494 { EL_AMOEBA_DROPPING, 2 },
1495 { EL_QUICKSAND_FILLING, 1 },
1496 { EL_QUICKSAND_EMPTYING, 1 },
1497 { EL_QUICKSAND_FAST_FILLING, 2 },
1498 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1499 { EL_MAGIC_WALL_FILLING, 2 },
1500 { EL_MAGIC_WALL_EMPTYING, 2 },
1501 { EL_BD_MAGIC_WALL_FILLING, 2 },
1502 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_DC_MAGIC_WALL_FILLING, 2 },
1504 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1506 { EL_UNDEFINED, 0 },
1514 collect_count_list[] =
1517 { EL_BD_DIAMOND, 1 },
1518 { EL_EMERALD_YELLOW, 1 },
1519 { EL_EMERALD_RED, 1 },
1520 { EL_EMERALD_PURPLE, 1 },
1522 { EL_SP_INFOTRON, 1 },
1526 { EL_UNDEFINED, 0 },
1534 access_direction_list[] =
1536 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1537 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1538 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1539 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1540 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1541 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1542 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1543 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1544 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1545 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1546 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1548 { EL_SP_PORT_LEFT, MV_RIGHT },
1549 { EL_SP_PORT_RIGHT, MV_LEFT },
1550 { EL_SP_PORT_UP, MV_DOWN },
1551 { EL_SP_PORT_DOWN, MV_UP },
1552 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1553 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1554 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1555 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1556 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1557 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1558 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1559 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1560 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1561 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1562 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1563 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1564 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1565 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1566 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1568 { EL_UNDEFINED, MV_NONE }
1571 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1573 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1574 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1575 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1576 IS_JUST_CHANGING(x, y))
1578 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1580 /* static variables for playfield scan mode (scanning forward or backward) */
1581 static int playfield_scan_start_x = 0;
1582 static int playfield_scan_start_y = 0;
1583 static int playfield_scan_delta_x = 1;
1584 static int playfield_scan_delta_y = 1;
1586 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1587 (y) >= 0 && (y) <= lev_fieldy - 1; \
1588 (y) += playfield_scan_delta_y) \
1589 for ((x) = playfield_scan_start_x; \
1590 (x) >= 0 && (x) <= lev_fieldx - 1; \
1591 (x) += playfield_scan_delta_x)
1594 void DEBUG_SetMaximumDynamite()
1598 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1599 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1600 local_player->inventory_element[local_player->inventory_size++] =
1605 static void InitPlayfieldScanModeVars()
1607 if (game.use_reverse_scan_direction)
1609 playfield_scan_start_x = lev_fieldx - 1;
1610 playfield_scan_start_y = lev_fieldy - 1;
1612 playfield_scan_delta_x = -1;
1613 playfield_scan_delta_y = -1;
1617 playfield_scan_start_x = 0;
1618 playfield_scan_start_y = 0;
1620 playfield_scan_delta_x = 1;
1621 playfield_scan_delta_y = 1;
1625 static void InitPlayfieldScanMode(int mode)
1627 game.use_reverse_scan_direction =
1628 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1630 InitPlayfieldScanModeVars();
1633 static int get_move_delay_from_stepsize(int move_stepsize)
1636 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1638 /* make sure that stepsize value is always a power of 2 */
1639 move_stepsize = (1 << log_2(move_stepsize));
1641 return TILEX / move_stepsize;
1644 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1647 int player_nr = player->index_nr;
1648 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1649 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1651 /* do no immediately change move delay -- the player might just be moving */
1652 player->move_delay_value_next = move_delay;
1654 /* information if player can move must be set separately */
1655 player->cannot_move = cannot_move;
1659 player->move_delay = game.initial_move_delay[player_nr];
1660 player->move_delay_value = game.initial_move_delay_value[player_nr];
1662 player->move_delay_value_next = -1;
1664 player->move_delay_reset_counter = 0;
1668 void GetPlayerConfig()
1670 GameFrameDelay = setup.game_frame_delay;
1672 if (!audio.sound_available)
1673 setup.sound_simple = FALSE;
1675 if (!audio.loops_available)
1676 setup.sound_loops = FALSE;
1678 if (!audio.music_available)
1679 setup.sound_music = FALSE;
1681 if (!video.fullscreen_available)
1682 setup.fullscreen = FALSE;
1684 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1686 SetAudioMode(setup.sound);
1690 int GetElementFromGroupElement(int element)
1692 if (IS_GROUP_ELEMENT(element))
1694 struct ElementGroupInfo *group = element_info[element].group;
1695 int last_anim_random_frame = gfx.anim_random_frame;
1698 if (group->choice_mode == ANIM_RANDOM)
1699 gfx.anim_random_frame = RND(group->num_elements_resolved);
1701 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1702 group->choice_mode, 0,
1705 if (group->choice_mode == ANIM_RANDOM)
1706 gfx.anim_random_frame = last_anim_random_frame;
1708 group->choice_pos++;
1710 element = group->element_resolved[element_pos];
1716 static void InitPlayerField(int x, int y, int element, boolean init_game)
1718 if (element == EL_SP_MURPHY)
1722 if (stored_player[0].present)
1724 Feld[x][y] = EL_SP_MURPHY_CLONE;
1730 stored_player[0].initial_element = element;
1731 stored_player[0].use_murphy = TRUE;
1733 if (!level.use_artwork_element[0])
1734 stored_player[0].artwork_element = EL_SP_MURPHY;
1737 Feld[x][y] = EL_PLAYER_1;
1743 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1744 int jx = player->jx, jy = player->jy;
1746 player->present = TRUE;
1748 player->block_last_field = (element == EL_SP_MURPHY ?
1749 level.sp_block_last_field :
1750 level.block_last_field);
1752 /* ---------- initialize player's last field block delay --------------- */
1754 /* always start with reliable default value (no adjustment needed) */
1755 player->block_delay_adjustment = 0;
1757 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1758 if (player->block_last_field && element == EL_SP_MURPHY)
1759 player->block_delay_adjustment = 1;
1761 /* special case 2: in game engines before 3.1.1, blocking was different */
1762 if (game.use_block_last_field_bug)
1763 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1765 if (!options.network || player->connected)
1767 player->active = TRUE;
1769 /* remove potentially duplicate players */
1770 if (StorePlayer[jx][jy] == Feld[x][y])
1771 StorePlayer[jx][jy] = 0;
1773 StorePlayer[x][y] = Feld[x][y];
1777 printf("Player %d activated.\n", player->element_nr);
1778 printf("[Local player is %d and currently %s.]\n",
1779 local_player->element_nr,
1780 local_player->active ? "active" : "not active");
1784 Feld[x][y] = EL_EMPTY;
1786 player->jx = player->last_jx = x;
1787 player->jy = player->last_jy = y;
1790 #if USE_PLAYER_REANIMATION
1793 int player_nr = GET_PLAYER_NR(element);
1794 struct PlayerInfo *player = &stored_player[player_nr];
1796 if (player->active && player->killed)
1797 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1802 static void InitField(int x, int y, boolean init_game)
1804 int element = Feld[x][y];
1813 InitPlayerField(x, y, element, init_game);
1816 case EL_SOKOBAN_FIELD_PLAYER:
1817 element = Feld[x][y] = EL_PLAYER_1;
1818 InitField(x, y, init_game);
1820 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1821 InitField(x, y, init_game);
1824 case EL_SOKOBAN_FIELD_EMPTY:
1825 local_player->sokobanfields_still_needed++;
1829 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1830 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1831 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1833 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1834 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847 case EL_SPACESHIP_RIGHT:
1848 case EL_SPACESHIP_UP:
1849 case EL_SPACESHIP_LEFT:
1850 case EL_SPACESHIP_DOWN:
1851 case EL_BD_BUTTERFLY:
1852 case EL_BD_BUTTERFLY_RIGHT:
1853 case EL_BD_BUTTERFLY_UP:
1854 case EL_BD_BUTTERFLY_LEFT:
1855 case EL_BD_BUTTERFLY_DOWN:
1857 case EL_BD_FIREFLY_RIGHT:
1858 case EL_BD_FIREFLY_UP:
1859 case EL_BD_FIREFLY_LEFT:
1860 case EL_BD_FIREFLY_DOWN:
1861 case EL_PACMAN_RIGHT:
1863 case EL_PACMAN_LEFT:
1864 case EL_PACMAN_DOWN:
1866 case EL_YAMYAM_LEFT:
1867 case EL_YAMYAM_RIGHT:
1869 case EL_YAMYAM_DOWN:
1870 case EL_DARK_YAMYAM:
1873 case EL_SP_SNIKSNAK:
1874 case EL_SP_ELECTRON:
1883 case EL_AMOEBA_FULL:
1888 case EL_AMOEBA_DROP:
1889 if (y == lev_fieldy - 1)
1891 Feld[x][y] = EL_AMOEBA_GROWING;
1892 Store[x][y] = EL_AMOEBA_WET;
1896 case EL_DYNAMITE_ACTIVE:
1897 case EL_SP_DISK_RED_ACTIVE:
1898 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1899 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1902 MovDelay[x][y] = 96;
1905 case EL_EM_DYNAMITE_ACTIVE:
1906 MovDelay[x][y] = 32;
1910 local_player->lights_still_needed++;
1914 local_player->friends_still_needed++;
1919 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1922 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1923 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1924 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1925 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1926 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1927 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1928 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1929 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1930 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1931 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1932 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1933 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1936 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1937 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1938 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1940 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1942 game.belt_dir[belt_nr] = belt_dir;
1943 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1945 else /* more than one switch -- set it like the first switch */
1947 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1952 #if !USE_BOTH_SWITCHGATE_SWITCHES
1953 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1955 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1958 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1960 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1964 case EL_LIGHT_SWITCH_ACTIVE:
1966 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1969 case EL_INVISIBLE_STEELWALL:
1970 case EL_INVISIBLE_WALL:
1971 case EL_INVISIBLE_SAND:
1972 if (game.light_time_left > 0 ||
1973 game.lenses_time_left > 0)
1974 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1977 case EL_EMC_MAGIC_BALL:
1978 if (game.ball_state)
1979 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1982 case EL_EMC_MAGIC_BALL_SWITCH:
1983 if (game.ball_state)
1984 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1987 case EL_TRIGGER_PLAYER:
1988 case EL_TRIGGER_ELEMENT:
1989 case EL_TRIGGER_CE_VALUE:
1990 case EL_TRIGGER_CE_SCORE:
1992 case EL_ANY_ELEMENT:
1993 case EL_CURRENT_CE_VALUE:
1994 case EL_CURRENT_CE_SCORE:
2011 /* reference elements should not be used on the playfield */
2012 Feld[x][y] = EL_EMPTY;
2016 if (IS_CUSTOM_ELEMENT(element))
2018 if (CAN_MOVE(element))
2021 #if USE_NEW_CUSTOM_VALUE
2022 if (!element_info[element].use_last_ce_value || init_game)
2023 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2026 else if (IS_GROUP_ELEMENT(element))
2028 Feld[x][y] = GetElementFromGroupElement(element);
2030 InitField(x, y, init_game);
2037 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2040 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2042 InitField(x, y, init_game);
2044 /* not needed to call InitMovDir() -- already done by InitField()! */
2045 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2046 CAN_MOVE(Feld[x][y]))
2050 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2052 int old_element = Feld[x][y];
2054 InitField(x, y, init_game);
2056 /* not needed to call InitMovDir() -- already done by InitField()! */
2057 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2058 CAN_MOVE(old_element) &&
2059 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2062 /* this case is in fact a combination of not less than three bugs:
2063 first, it calls InitMovDir() for elements that can move, although this is
2064 already done by InitField(); then, it checks the element that was at this
2065 field _before_ the call to InitField() (which can change it); lastly, it
2066 was not called for "mole with direction" elements, which were treated as
2067 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2073 static int get_key_element_from_nr(int key_nr)
2075 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2076 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2077 EL_EM_KEY_1 : EL_KEY_1);
2079 return key_base_element + key_nr;
2082 static int get_next_dropped_element(struct PlayerInfo *player)
2084 return (player->inventory_size > 0 ?
2085 player->inventory_element[player->inventory_size - 1] :
2086 player->inventory_infinite_element != EL_UNDEFINED ?
2087 player->inventory_infinite_element :
2088 player->dynabombs_left > 0 ?
2089 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2093 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2095 /* pos >= 0: get element from bottom of the stack;
2096 pos < 0: get element from top of the stack */
2100 int min_inventory_size = -pos;
2101 int inventory_pos = player->inventory_size - min_inventory_size;
2102 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2104 return (player->inventory_size >= min_inventory_size ?
2105 player->inventory_element[inventory_pos] :
2106 player->inventory_infinite_element != EL_UNDEFINED ?
2107 player->inventory_infinite_element :
2108 player->dynabombs_left >= min_dynabombs_left ?
2109 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2114 int min_dynabombs_left = pos + 1;
2115 int min_inventory_size = pos + 1 - player->dynabombs_left;
2116 int inventory_pos = pos - player->dynabombs_left;
2118 return (player->inventory_infinite_element != EL_UNDEFINED ?
2119 player->inventory_infinite_element :
2120 player->dynabombs_left >= min_dynabombs_left ?
2121 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2122 player->inventory_size >= min_inventory_size ?
2123 player->inventory_element[inventory_pos] :
2128 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2130 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2131 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2134 if (gpo1->sort_priority != gpo2->sort_priority)
2135 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2137 compare_result = gpo1->nr - gpo2->nr;
2139 return compare_result;
2142 void InitGameControlValues()
2146 for (i = 0; game_panel_controls[i].nr != -1; i++)
2148 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2149 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2150 struct TextPosInfo *pos = gpc->pos;
2152 int type = gpc->type;
2156 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2157 Error(ERR_EXIT, "this should not happen -- please debug");
2160 /* force update of game controls after initialization */
2161 gpc->value = gpc->last_value = -1;
2162 gpc->frame = gpc->last_frame = -1;
2163 gpc->gfx_frame = -1;
2165 /* determine panel value width for later calculation of alignment */
2166 if (type == TYPE_INTEGER || type == TYPE_STRING)
2168 pos->width = pos->size * getFontWidth(pos->font);
2169 pos->height = getFontHeight(pos->font);
2171 else if (type == TYPE_ELEMENT)
2173 pos->width = pos->size;
2174 pos->height = pos->size;
2177 /* fill structure for game panel draw order */
2179 gpo->sort_priority = pos->sort_priority;
2182 /* sort game panel controls according to sort_priority and control number */
2183 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2184 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2187 void UpdatePlayfieldElementCount()
2189 boolean use_element_count = FALSE;
2192 /* first check if it is needed at all to calculate playfield element count */
2193 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2194 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2195 use_element_count = TRUE;
2197 if (!use_element_count)
2200 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2201 element_info[i].element_count = 0;
2203 SCAN_PLAYFIELD(x, y)
2205 element_info[Feld[x][y]].element_count++;
2208 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2209 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2210 if (IS_IN_GROUP(j, i))
2211 element_info[EL_GROUP_START + i].element_count +=
2212 element_info[j].element_count;
2215 void UpdateGameControlValues()
2218 int time = (local_player->LevelSolved ?
2219 local_player->LevelSolved_CountingTime :
2220 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2221 level.native_em_level->lev->time :
2222 level.time == 0 ? TimePlayed : TimeLeft);
2223 int score = (local_player->LevelSolved ?
2224 local_player->LevelSolved_CountingScore :
2225 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226 level.native_em_level->lev->score :
2227 local_player->score);
2228 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 level.native_em_level->lev->required :
2230 local_player->gems_still_needed);
2231 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2232 level.native_em_level->lev->required > 0 :
2233 local_player->gems_still_needed > 0 ||
2234 local_player->sokobanfields_still_needed > 0 ||
2235 local_player->lights_still_needed > 0);
2237 UpdatePlayfieldElementCount();
2239 /* update game panel control values */
2241 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2242 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2244 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2245 for (i = 0; i < MAX_NUM_KEYS; i++)
2246 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2247 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2248 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2250 if (game.centered_player_nr == -1)
2252 for (i = 0; i < MAX_PLAYERS; i++)
2254 for (k = 0; k < MAX_NUM_KEYS; k++)
2256 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2258 if (level.native_em_level->ply[i]->keys & (1 << k))
2259 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2260 get_key_element_from_nr(k);
2262 else if (stored_player[i].key[k])
2263 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2264 get_key_element_from_nr(k);
2267 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2268 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2269 level.native_em_level->ply[i]->dynamite;
2271 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2272 stored_player[i].inventory_size;
2274 if (stored_player[i].num_white_keys > 0)
2275 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2278 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2279 stored_player[i].num_white_keys;
2284 int player_nr = game.centered_player_nr;
2286 for (k = 0; k < MAX_NUM_KEYS; k++)
2288 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2290 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2291 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2292 get_key_element_from_nr(k);
2294 else if (stored_player[player_nr].key[k])
2295 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2296 get_key_element_from_nr(k);
2299 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2300 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2301 level.native_em_level->ply[player_nr]->dynamite;
2303 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2304 stored_player[player_nr].inventory_size;
2306 if (stored_player[player_nr].num_white_keys > 0)
2307 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2309 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2310 stored_player[player_nr].num_white_keys;
2313 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2315 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2316 get_inventory_element_from_pos(local_player, i);
2317 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2318 get_inventory_element_from_pos(local_player, -i - 1);
2321 game_panel_controls[GAME_PANEL_SCORE].value = score;
2322 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2324 game_panel_controls[GAME_PANEL_TIME].value = time;
2326 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2327 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2328 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2330 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2331 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2333 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2334 local_player->shield_normal_time_left;
2335 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2336 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2338 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2339 local_player->shield_deadly_time_left;
2341 game_panel_controls[GAME_PANEL_EXIT].value =
2342 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2344 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2345 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2346 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2347 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2348 EL_EMC_MAGIC_BALL_SWITCH);
2350 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2351 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2352 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2353 game.light_time_left;
2355 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2356 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2357 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2358 game.timegate_time_left;
2360 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2361 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2363 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2364 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2365 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2366 game.lenses_time_left;
2368 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2369 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2370 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2371 game.magnify_time_left;
2373 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2374 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2375 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2376 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2377 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2378 EL_BALLOON_SWITCH_NONE);
2380 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2381 local_player->dynabomb_count;
2382 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2383 local_player->dynabomb_size;
2384 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2385 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2387 game_panel_controls[GAME_PANEL_PENGUINS].value =
2388 local_player->friends_still_needed;
2390 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2391 local_player->sokobanfields_still_needed;
2392 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2393 local_player->sokobanfields_still_needed;
2395 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2396 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2398 for (i = 0; i < NUM_BELTS; i++)
2400 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2401 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2402 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2403 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2404 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2407 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2408 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2409 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2410 game.magic_wall_time_left;
2412 #if USE_PLAYER_GRAVITY
2413 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2414 local_player->gravity;
2416 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2419 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2420 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2422 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2423 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2424 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2425 game.panel.element[i].id : EL_UNDEFINED);
2427 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2428 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2429 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2430 element_info[game.panel.element_count[i].id].element_count : 0);
2432 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2433 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2434 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2435 element_info[game.panel.ce_score[i].id].collect_score : 0);
2437 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2438 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2439 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2440 element_info[game.panel.ce_score_element[i].id].collect_score :
2443 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2444 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2445 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2447 /* update game panel control frames */
2449 for (i = 0; game_panel_controls[i].nr != -1; i++)
2451 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2453 if (gpc->type == TYPE_ELEMENT)
2455 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2457 int last_anim_random_frame = gfx.anim_random_frame;
2458 int element = gpc->value;
2459 int graphic = el2panelimg(element);
2461 if (gpc->value != gpc->last_value)
2464 gpc->gfx_random = INIT_GFX_RANDOM();
2470 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2471 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2472 gpc->gfx_random = INIT_GFX_RANDOM();
2475 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2476 gfx.anim_random_frame = gpc->gfx_random;
2478 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2479 gpc->gfx_frame = element_info[element].collect_score;
2481 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2484 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2485 gfx.anim_random_frame = last_anim_random_frame;
2491 void DisplayGameControlValues()
2493 boolean redraw_panel = FALSE;
2496 for (i = 0; game_panel_controls[i].nr != -1; i++)
2498 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2500 if (PANEL_DEACTIVATED(gpc->pos))
2503 if (gpc->value == gpc->last_value &&
2504 gpc->frame == gpc->last_frame)
2507 redraw_panel = TRUE;
2513 /* copy default game door content to main double buffer */
2514 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2515 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2517 /* redraw game control buttons */
2519 RedrawGameButtons();
2525 game_status = GAME_MODE_PSEUDO_PANEL;
2528 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2530 for (i = 0; game_panel_controls[i].nr != -1; i++)
2534 int nr = game_panel_order[i].nr;
2535 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2540 struct TextPosInfo *pos = gpc->pos;
2541 int type = gpc->type;
2542 int value = gpc->value;
2543 int frame = gpc->frame;
2545 int last_value = gpc->last_value;
2546 int last_frame = gpc->last_frame;
2548 int size = pos->size;
2549 int font = pos->font;
2550 boolean draw_masked = pos->draw_masked;
2551 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2553 if (PANEL_DEACTIVATED(pos))
2557 if (value == last_value && frame == last_frame)
2561 gpc->last_value = value;
2562 gpc->last_frame = frame;
2565 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2568 if (type == TYPE_INTEGER)
2570 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2571 nr == GAME_PANEL_TIME)
2573 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2575 if (use_dynamic_size) /* use dynamic number of digits */
2577 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2578 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2579 int size2 = size1 + 1;
2580 int font1 = pos->font;
2581 int font2 = pos->font_alt;
2583 size = (value < value_change ? size1 : size2);
2584 font = (value < value_change ? font1 : font2);
2587 /* clear background if value just changed its size (dynamic digits) */
2588 if ((last_value < value_change) != (value < value_change))
2590 int width1 = size1 * getFontWidth(font1);
2591 int width2 = size2 * getFontWidth(font2);
2592 int max_width = MAX(width1, width2);
2593 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2595 pos->width = max_width;
2597 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2598 max_width, max_height);
2605 /* correct text size if "digits" is zero or less */
2607 size = strlen(int2str(value, size));
2609 /* dynamically correct text alignment */
2610 pos->width = size * getFontWidth(font);
2613 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2614 int2str(value, size), font, mask_mode);
2616 else if (type == TYPE_ELEMENT)
2618 int element, graphic;
2622 int dst_x = PANEL_XPOS(pos);
2623 int dst_y = PANEL_YPOS(pos);
2626 if (value != EL_UNDEFINED && value != EL_EMPTY)
2629 graphic = el2panelimg(value);
2631 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2634 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2638 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2641 width = graphic_info[graphic].width * size / TILESIZE;
2642 height = graphic_info[graphic].height * size / TILESIZE;
2646 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2647 dst_x - src_x, dst_y - src_y);
2648 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2653 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2658 if (value == EL_UNDEFINED || value == EL_EMPTY)
2660 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2661 graphic = el2panelimg(element);
2663 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2664 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2665 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2670 graphic = el2panelimg(value);
2672 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2675 width = graphic_info[graphic].width * size / TILESIZE;
2676 height = graphic_info[graphic].height * size / TILESIZE;
2678 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2681 else if (type == TYPE_STRING)
2683 boolean active = (value != 0);
2684 char *state_normal = "off";
2685 char *state_active = "on";
2686 char *state = (active ? state_active : state_normal);
2687 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2688 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2689 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2690 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2692 if (nr == GAME_PANEL_GRAVITY_STATE)
2694 int font1 = pos->font; /* (used for normal state) */
2695 int font2 = pos->font_alt; /* (used for active state) */
2697 int size1 = strlen(state_normal);
2698 int size2 = strlen(state_active);
2699 int width1 = size1 * getFontWidth(font1);
2700 int width2 = size2 * getFontWidth(font2);
2701 int max_width = MAX(width1, width2);
2702 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2704 pos->width = max_width;
2706 /* clear background for values that may have changed its size */
2707 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2708 max_width, max_height);
2711 font = (active ? font2 : font1);
2721 /* don't truncate output if "chars" is zero or less */
2724 /* dynamically correct text alignment */
2725 pos->width = size * getFontWidth(font);
2729 s_cut = getStringCopyN(s, size);
2731 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2732 s_cut, font, mask_mode);
2738 redraw_mask |= REDRAW_DOOR_1;
2741 game_status = GAME_MODE_PLAYING;
2744 void UpdateAndDisplayGameControlValues()
2746 if (tape.warp_forward)
2749 UpdateGameControlValues();
2750 DisplayGameControlValues();
2753 void DrawGameValue_Emeralds(int value)
2755 struct TextPosInfo *pos = &game.panel.gems;
2757 int font_nr = pos->font;
2759 int font_nr = FONT_TEXT_2;
2761 int font_width = getFontWidth(font_nr);
2762 int chars = pos->size;
2765 return; /* !!! USE NEW STUFF !!! */
2768 if (PANEL_DEACTIVATED(pos))
2771 pos->width = chars * font_width;
2773 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2776 void DrawGameValue_Dynamite(int value)
2778 struct TextPosInfo *pos = &game.panel.inventory_count;
2780 int font_nr = pos->font;
2782 int font_nr = FONT_TEXT_2;
2784 int font_width = getFontWidth(font_nr);
2785 int chars = pos->size;
2788 return; /* !!! USE NEW STUFF !!! */
2791 if (PANEL_DEACTIVATED(pos))
2794 pos->width = chars * font_width;
2796 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2799 void DrawGameValue_Score(int value)
2801 struct TextPosInfo *pos = &game.panel.score;
2803 int font_nr = pos->font;
2805 int font_nr = FONT_TEXT_2;
2807 int font_width = getFontWidth(font_nr);
2808 int chars = pos->size;
2811 return; /* !!! USE NEW STUFF !!! */
2814 if (PANEL_DEACTIVATED(pos))
2817 pos->width = chars * font_width;
2819 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2822 void DrawGameValue_Time(int value)
2824 struct TextPosInfo *pos = &game.panel.time;
2825 static int last_value = -1;
2828 int chars = pos->size;
2830 int font1_nr = pos->font;
2831 int font2_nr = pos->font_alt;
2833 int font1_nr = FONT_TEXT_2;
2834 int font2_nr = FONT_TEXT_1;
2836 int font_nr = font1_nr;
2837 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2840 return; /* !!! USE NEW STUFF !!! */
2843 if (PANEL_DEACTIVATED(pos))
2846 if (use_dynamic_chars) /* use dynamic number of chars */
2848 chars = (value < 1000 ? chars1 : chars2);
2849 font_nr = (value < 1000 ? font1_nr : font2_nr);
2852 /* clear background if value just changed its size (dynamic chars only) */
2853 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2855 int width1 = chars1 * getFontWidth(font1_nr);
2856 int width2 = chars2 * getFontWidth(font2_nr);
2857 int max_width = MAX(width1, width2);
2858 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2860 pos->width = max_width;
2862 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2863 max_width, max_height);
2866 pos->width = chars * getFontWidth(font_nr);
2868 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2873 void DrawGameValue_Level(int value)
2875 struct TextPosInfo *pos = &game.panel.level_number;
2878 int chars = pos->size;
2880 int font1_nr = pos->font;
2881 int font2_nr = pos->font_alt;
2883 int font1_nr = FONT_TEXT_2;
2884 int font2_nr = FONT_TEXT_1;
2886 int font_nr = font1_nr;
2887 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2890 return; /* !!! USE NEW STUFF !!! */
2893 if (PANEL_DEACTIVATED(pos))
2896 if (use_dynamic_chars) /* use dynamic number of chars */
2898 chars = (level_nr < 100 ? chars1 : chars2);
2899 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2902 pos->width = chars * getFontWidth(font_nr);
2904 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2907 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2910 struct TextPosInfo *pos = &game.panel.keys;
2913 int base_key_graphic = EL_KEY_1;
2918 return; /* !!! USE NEW STUFF !!! */
2922 if (PANEL_DEACTIVATED(pos))
2927 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2928 base_key_graphic = EL_EM_KEY_1;
2932 pos->width = 4 * MINI_TILEX;
2936 for (i = 0; i < MAX_NUM_KEYS; i++)
2938 /* currently only 4 of 8 possible keys are displayed */
2939 for (i = 0; i < STD_NUM_KEYS; i++)
2943 struct TextPosInfo *pos = &game.panel.key[i];
2945 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2946 int src_y = DOOR_GFX_PAGEY1 + 123;
2948 int dst_x = PANEL_XPOS(pos);
2949 int dst_y = PANEL_YPOS(pos);
2951 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2952 int dst_y = PANEL_YPOS(pos);
2956 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2957 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2959 int graphic = el2edimg(element);
2963 if (PANEL_DEACTIVATED(pos))
2968 /* masked blit with tiles from half-size scaled bitmap does not work yet
2969 (no mask bitmap created for these sizes after loading and scaling) --
2970 solution: load without creating mask, scale, then create final mask */
2972 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2973 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2978 int graphic = el2edimg(base_key_graphic + i);
2983 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2985 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2986 dst_x - src_x, dst_y - src_y);
2987 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2993 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2995 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2996 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2999 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3001 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3002 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3010 void DrawGameValue_Emeralds(int value)
3012 int font_nr = FONT_TEXT_2;
3013 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3015 if (PANEL_DEACTIVATED(game.panel.gems))
3018 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3021 void DrawGameValue_Dynamite(int value)
3023 int font_nr = FONT_TEXT_2;
3024 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3026 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3029 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3032 void DrawGameValue_Score(int value)
3034 int font_nr = FONT_TEXT_2;
3035 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3037 if (PANEL_DEACTIVATED(game.panel.score))
3040 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3043 void DrawGameValue_Time(int value)
3045 int font1_nr = FONT_TEXT_2;
3047 int font2_nr = FONT_TEXT_1;
3049 int font2_nr = FONT_LEVEL_NUMBER;
3051 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3052 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3054 if (PANEL_DEACTIVATED(game.panel.time))
3057 /* clear background if value just changed its size */
3058 if (value == 999 || value == 1000)
3059 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3062 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3064 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3067 void DrawGameValue_Level(int value)
3069 int font1_nr = FONT_TEXT_2;
3071 int font2_nr = FONT_TEXT_1;
3073 int font2_nr = FONT_LEVEL_NUMBER;
3076 if (PANEL_DEACTIVATED(game.panel.level))
3080 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3082 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3085 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3087 int base_key_graphic = EL_KEY_1;
3090 if (PANEL_DEACTIVATED(game.panel.keys))
3093 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3094 base_key_graphic = EL_EM_KEY_1;
3096 /* currently only 4 of 8 possible keys are displayed */
3097 for (i = 0; i < STD_NUM_KEYS; i++)
3099 int x = XX_KEYS + i * MINI_TILEX;
3103 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3105 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3106 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3112 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3115 int key[MAX_NUM_KEYS];
3118 /* prevent EM engine from updating time/score values parallel to GameWon() */
3119 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3120 local_player->LevelSolved)
3123 for (i = 0; i < MAX_NUM_KEYS; i++)
3124 key[i] = key_bits & (1 << i);
3126 DrawGameValue_Level(level_nr);
3128 DrawGameValue_Emeralds(emeralds);
3129 DrawGameValue_Dynamite(dynamite);
3130 DrawGameValue_Score(score);
3131 DrawGameValue_Time(time);
3133 DrawGameValue_Keys(key);
3136 void UpdateGameDoorValues()
3138 UpdateGameControlValues();
3141 void DrawGameDoorValues()
3143 DisplayGameControlValues();
3146 void DrawGameDoorValues_OLD()
3148 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3149 int dynamite_value = 0;
3150 int score_value = (local_player->LevelSolved ? local_player->score_final :
3151 local_player->score);
3152 int gems_value = local_player->gems_still_needed;
3156 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3158 DrawGameDoorValues_EM();
3163 if (game.centered_player_nr == -1)
3165 for (i = 0; i < MAX_PLAYERS; i++)
3167 for (j = 0; j < MAX_NUM_KEYS; j++)
3168 if (stored_player[i].key[j])
3169 key_bits |= (1 << j);
3171 dynamite_value += stored_player[i].inventory_size;
3176 int player_nr = game.centered_player_nr;
3178 for (i = 0; i < MAX_NUM_KEYS; i++)
3179 if (stored_player[player_nr].key[i])
3180 key_bits |= (1 << i);
3182 dynamite_value = stored_player[player_nr].inventory_size;
3185 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3191 =============================================================================
3193 -----------------------------------------------------------------------------
3194 initialize game engine due to level / tape version number
3195 =============================================================================
3198 static void InitGameEngine()
3200 int i, j, k, l, x, y;
3202 /* set game engine from tape file when re-playing, else from level file */
3203 game.engine_version = (tape.playing ? tape.engine_version :
3204 level.game_version);
3206 /* ---------------------------------------------------------------------- */
3207 /* set flags for bugs and changes according to active game engine version */
3208 /* ---------------------------------------------------------------------- */
3211 Summary of bugfix/change:
3212 Fixed handling for custom elements that change when pushed by the player.
3214 Fixed/changed in version:
3218 Before 3.1.0, custom elements that "change when pushing" changed directly
3219 after the player started pushing them (until then handled in "DigField()").
3220 Since 3.1.0, these custom elements are not changed until the "pushing"
3221 move of the element is finished (now handled in "ContinueMoving()").
3223 Affected levels/tapes:
3224 The first condition is generally needed for all levels/tapes before version
3225 3.1.0, which might use the old behaviour before it was changed; known tapes
3226 that are affected are some tapes from the level set "Walpurgis Gardens" by
3228 The second condition is an exception from the above case and is needed for
3229 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3230 above (including some development versions of 3.1.0), but before it was
3231 known that this change would break tapes like the above and was fixed in
3232 3.1.1, so that the changed behaviour was active although the engine version
3233 while recording maybe was before 3.1.0. There is at least one tape that is
3234 affected by this exception, which is the tape for the one-level set "Bug
3235 Machine" by Juergen Bonhagen.
3238 game.use_change_when_pushing_bug =
3239 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3241 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3242 tape.game_version < VERSION_IDENT(3,1,1,0)));
3245 Summary of bugfix/change:
3246 Fixed handling for blocking the field the player leaves when moving.
3248 Fixed/changed in version:
3252 Before 3.1.1, when "block last field when moving" was enabled, the field
3253 the player is leaving when moving was blocked for the time of the move,
3254 and was directly unblocked afterwards. This resulted in the last field
3255 being blocked for exactly one less than the number of frames of one player
3256 move. Additionally, even when blocking was disabled, the last field was
3257 blocked for exactly one frame.
3258 Since 3.1.1, due to changes in player movement handling, the last field
3259 is not blocked at all when blocking is disabled. When blocking is enabled,
3260 the last field is blocked for exactly the number of frames of one player
3261 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3262 last field is blocked for exactly one more than the number of frames of
3265 Affected levels/tapes:
3266 (!!! yet to be determined -- probably many !!!)
3269 game.use_block_last_field_bug =
3270 (game.engine_version < VERSION_IDENT(3,1,1,0));
3273 Summary of bugfix/change:
3274 Changed behaviour of CE changes with multiple changes per single frame.
3276 Fixed/changed in version:
3280 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3281 This resulted in race conditions where CEs seem to behave strange in some
3282 situations (where triggered CE changes were just skipped because there was
3283 already a CE change on that tile in the playfield in that engine frame).
3284 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3285 (The number of changes per frame must be limited in any case, because else
3286 it is easily possible to define CE changes that would result in an infinite
3287 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3288 should be set large enough so that it would only be reached in cases where
3289 the corresponding CE change conditions run into a loop. Therefore, it seems
3290 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3291 maximal number of change pages for custom elements.)
3293 Affected levels/tapes:
3297 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3298 game.max_num_changes_per_frame = 1;
3300 game.max_num_changes_per_frame =
3301 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3304 /* ---------------------------------------------------------------------- */
3306 /* default scan direction: scan playfield from top/left to bottom/right */
3307 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3309 /* dynamically adjust element properties according to game engine version */
3310 InitElementPropertiesEngine(game.engine_version);
3313 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3314 printf(" tape version == %06d [%s] [file: %06d]\n",
3315 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3317 printf(" => game.engine_version == %06d\n", game.engine_version);
3320 /* ---------- initialize player's initial move delay --------------------- */
3322 /* dynamically adjust player properties according to level information */
3323 for (i = 0; i < MAX_PLAYERS; i++)
3324 game.initial_move_delay_value[i] =
3325 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3327 /* dynamically adjust player properties according to game engine version */
3328 for (i = 0; i < MAX_PLAYERS; i++)
3329 game.initial_move_delay[i] =
3330 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3331 game.initial_move_delay_value[i] : 0);
3333 /* ---------- initialize player's initial push delay --------------------- */
3335 /* dynamically adjust player properties according to game engine version */
3336 game.initial_push_delay_value =
3337 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3339 /* ---------- initialize changing elements ------------------------------- */
3341 /* initialize changing elements information */
3342 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3344 struct ElementInfo *ei = &element_info[i];
3346 /* this pointer might have been changed in the level editor */
3347 ei->change = &ei->change_page[0];
3349 if (!IS_CUSTOM_ELEMENT(i))
3351 ei->change->target_element = EL_EMPTY_SPACE;
3352 ei->change->delay_fixed = 0;
3353 ei->change->delay_random = 0;
3354 ei->change->delay_frames = 1;
3357 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3359 ei->has_change_event[j] = FALSE;
3361 ei->event_page_nr[j] = 0;
3362 ei->event_page[j] = &ei->change_page[0];
3366 /* add changing elements from pre-defined list */
3367 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3369 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3370 struct ElementInfo *ei = &element_info[ch_delay->element];
3372 ei->change->target_element = ch_delay->target_element;
3373 ei->change->delay_fixed = ch_delay->change_delay;
3375 ei->change->pre_change_function = ch_delay->pre_change_function;
3376 ei->change->change_function = ch_delay->change_function;
3377 ei->change->post_change_function = ch_delay->post_change_function;
3379 ei->change->can_change = TRUE;
3380 ei->change->can_change_or_has_action = TRUE;
3382 ei->has_change_event[CE_DELAY] = TRUE;
3384 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3385 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3388 /* ---------- initialize internal run-time variables --------------------- */
3390 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3392 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3394 for (j = 0; j < ei->num_change_pages; j++)
3396 ei->change_page[j].can_change_or_has_action =
3397 (ei->change_page[j].can_change |
3398 ei->change_page[j].has_action);
3402 /* add change events from custom element configuration */
3403 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3405 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3407 for (j = 0; j < ei->num_change_pages; j++)
3409 if (!ei->change_page[j].can_change_or_has_action)
3412 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3414 /* only add event page for the first page found with this event */
3415 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3417 ei->has_change_event[k] = TRUE;
3419 ei->event_page_nr[k] = j;
3420 ei->event_page[k] = &ei->change_page[j];
3427 /* ---------- initialize reference elements in change conditions --------- */
3429 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3431 int element = EL_CUSTOM_START + i;
3432 struct ElementInfo *ei = &element_info[element];
3434 for (j = 0; j < ei->num_change_pages; j++)
3436 int trigger_element = ei->change_page[j].initial_trigger_element;
3438 if (trigger_element >= EL_PREV_CE_8 &&
3439 trigger_element <= EL_NEXT_CE_8)
3440 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3442 ei->change_page[j].trigger_element = trigger_element;
3447 /* ---------- initialize run-time trigger player and element ------------- */
3449 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3451 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3453 for (j = 0; j < ei->num_change_pages; j++)
3455 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3456 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3457 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3458 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3459 ei->change_page[j].actual_trigger_ce_value = 0;
3460 ei->change_page[j].actual_trigger_ce_score = 0;
3464 /* ---------- initialize trigger events ---------------------------------- */
3466 /* initialize trigger events information */
3467 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3468 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3469 trigger_events[i][j] = FALSE;
3471 /* add trigger events from element change event properties */
3472 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3474 struct ElementInfo *ei = &element_info[i];
3476 for (j = 0; j < ei->num_change_pages; j++)
3478 if (!ei->change_page[j].can_change_or_has_action)
3481 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3483 int trigger_element = ei->change_page[j].trigger_element;
3485 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3487 if (ei->change_page[j].has_event[k])
3489 if (IS_GROUP_ELEMENT(trigger_element))
3491 struct ElementGroupInfo *group =
3492 element_info[trigger_element].group;
3494 for (l = 0; l < group->num_elements_resolved; l++)
3495 trigger_events[group->element_resolved[l]][k] = TRUE;
3497 else if (trigger_element == EL_ANY_ELEMENT)
3498 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3499 trigger_events[l][k] = TRUE;
3501 trigger_events[trigger_element][k] = TRUE;
3508 /* ---------- initialize push delay -------------------------------------- */
3510 /* initialize push delay values to default */
3511 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3513 if (!IS_CUSTOM_ELEMENT(i))
3515 /* set default push delay values (corrected since version 3.0.7-1) */
3516 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3518 element_info[i].push_delay_fixed = 2;
3519 element_info[i].push_delay_random = 8;
3523 element_info[i].push_delay_fixed = 8;
3524 element_info[i].push_delay_random = 8;
3529 /* set push delay value for certain elements from pre-defined list */
3530 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3532 int e = push_delay_list[i].element;
3534 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3535 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3538 /* set push delay value for Supaplex elements for newer engine versions */
3539 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3541 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3543 if (IS_SP_ELEMENT(i))
3545 /* set SP push delay to just enough to push under a falling zonk */
3546 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3548 element_info[i].push_delay_fixed = delay;
3549 element_info[i].push_delay_random = 0;
3554 /* ---------- initialize move stepsize ----------------------------------- */
3556 /* initialize move stepsize values to default */
3557 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3558 if (!IS_CUSTOM_ELEMENT(i))
3559 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3561 /* set move stepsize value for certain elements from pre-defined list */
3562 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3564 int e = move_stepsize_list[i].element;
3566 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3569 /* ---------- initialize collect score ----------------------------------- */
3571 /* initialize collect score values for custom elements from initial value */
3572 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3573 if (IS_CUSTOM_ELEMENT(i))
3574 element_info[i].collect_score = element_info[i].collect_score_initial;
3576 /* ---------- initialize collect count ----------------------------------- */
3578 /* initialize collect count values for non-custom elements */
3579 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3580 if (!IS_CUSTOM_ELEMENT(i))
3581 element_info[i].collect_count_initial = 0;
3583 /* add collect count values for all elements from pre-defined list */
3584 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3585 element_info[collect_count_list[i].element].collect_count_initial =
3586 collect_count_list[i].count;
3588 /* ---------- initialize access direction -------------------------------- */
3590 /* initialize access direction values to default (access from every side) */
3591 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3592 if (!IS_CUSTOM_ELEMENT(i))
3593 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3595 /* set access direction value for certain elements from pre-defined list */
3596 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3597 element_info[access_direction_list[i].element].access_direction =
3598 access_direction_list[i].direction;
3600 /* ---------- initialize explosion content ------------------------------- */
3601 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3603 if (IS_CUSTOM_ELEMENT(i))
3606 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3608 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3610 element_info[i].content.e[x][y] =
3611 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3612 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3613 i == EL_PLAYER_3 ? EL_EMERALD :
3614 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3615 i == EL_MOLE ? EL_EMERALD_RED :
3616 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3617 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3618 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3619 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3620 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3621 i == EL_WALL_EMERALD ? EL_EMERALD :
3622 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3623 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3624 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3625 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3626 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3627 i == EL_WALL_PEARL ? EL_PEARL :
3628 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3633 /* ---------- initialize recursion detection ------------------------------ */
3634 recursion_loop_depth = 0;
3635 recursion_loop_detected = FALSE;
3636 recursion_loop_element = EL_UNDEFINED;
3638 /* ---------- initialize graphics engine ---------------------------------- */
3639 game.scroll_delay_value =
3640 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3641 setup.scroll_delay ? setup.scroll_delay_value : 0);
3642 game.scroll_delay_value =
3643 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3646 int get_num_special_action(int element, int action_first, int action_last)
3648 int num_special_action = 0;
3651 for (i = action_first; i <= action_last; i++)
3653 boolean found = FALSE;
3655 for (j = 0; j < NUM_DIRECTIONS; j++)
3656 if (el_act_dir2img(element, i, j) !=
3657 el_act_dir2img(element, ACTION_DEFAULT, j))
3661 num_special_action++;
3666 return num_special_action;
3671 =============================================================================
3673 -----------------------------------------------------------------------------
3674 initialize and start new game
3675 =============================================================================
3680 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3681 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3682 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3684 boolean do_fading = (game_status == GAME_MODE_MAIN);
3687 int initial_move_dir = MV_DOWN;
3689 int initial_move_dir = MV_NONE;
3693 game_status = GAME_MODE_PLAYING;
3696 InitGameControlValues();
3698 /* don't play tapes over network */
3699 network_playing = (options.network && !tape.playing);
3701 for (i = 0; i < MAX_PLAYERS; i++)
3703 struct PlayerInfo *player = &stored_player[i];
3705 player->index_nr = i;
3706 player->index_bit = (1 << i);
3707 player->element_nr = EL_PLAYER_1 + i;
3709 player->present = FALSE;
3710 player->active = FALSE;
3711 player->killed = FALSE;
3712 player->reanimated = FALSE;
3715 player->effective_action = 0;
3716 player->programmed_action = 0;
3719 player->score_final = 0;
3721 player->gems_still_needed = level.gems_needed;
3722 player->sokobanfields_still_needed = 0;
3723 player->lights_still_needed = 0;
3724 player->friends_still_needed = 0;
3726 for (j = 0; j < MAX_NUM_KEYS; j++)
3727 player->key[j] = FALSE;
3729 player->num_white_keys = 0;
3731 player->dynabomb_count = 0;
3732 player->dynabomb_size = 1;
3733 player->dynabombs_left = 0;
3734 player->dynabomb_xl = FALSE;
3736 player->MovDir = initial_move_dir;
3739 player->GfxDir = initial_move_dir;
3740 player->GfxAction = ACTION_DEFAULT;
3742 player->StepFrame = 0;
3744 player->initial_element = player->element_nr;
3745 player->artwork_element =
3746 (level.use_artwork_element[i] ? level.artwork_element[i] :
3747 player->element_nr);
3748 player->use_murphy = FALSE;
3750 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3751 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3753 player->gravity = level.initial_player_gravity[i];
3755 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3757 player->actual_frame_counter = 0;
3759 player->step_counter = 0;
3761 player->last_move_dir = initial_move_dir;
3763 player->is_active = FALSE;
3765 player->is_waiting = FALSE;
3766 player->is_moving = FALSE;
3767 player->is_auto_moving = FALSE;
3768 player->is_digging = FALSE;
3769 player->is_snapping = FALSE;
3770 player->is_collecting = FALSE;
3771 player->is_pushing = FALSE;
3772 player->is_switching = FALSE;
3773 player->is_dropping = FALSE;
3774 player->is_dropping_pressed = FALSE;
3776 player->is_bored = FALSE;
3777 player->is_sleeping = FALSE;
3779 player->frame_counter_bored = -1;
3780 player->frame_counter_sleeping = -1;
3782 player->anim_delay_counter = 0;
3783 player->post_delay_counter = 0;
3785 player->dir_waiting = initial_move_dir;
3786 player->action_waiting = ACTION_DEFAULT;
3787 player->last_action_waiting = ACTION_DEFAULT;
3788 player->special_action_bored = ACTION_DEFAULT;
3789 player->special_action_sleeping = ACTION_DEFAULT;
3791 player->switch_x = -1;
3792 player->switch_y = -1;
3794 player->drop_x = -1;
3795 player->drop_y = -1;
3797 player->show_envelope = 0;
3799 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3801 player->push_delay = -1; /* initialized when pushing starts */
3802 player->push_delay_value = game.initial_push_delay_value;
3804 player->drop_delay = 0;
3805 player->drop_pressed_delay = 0;
3807 player->last_jx = -1;
3808 player->last_jy = -1;
3812 player->shield_normal_time_left = 0;
3813 player->shield_deadly_time_left = 0;
3815 player->inventory_infinite_element = EL_UNDEFINED;
3816 player->inventory_size = 0;
3818 if (level.use_initial_inventory[i])
3820 for (j = 0; j < level.initial_inventory_size[i]; j++)
3822 int element = level.initial_inventory_content[i][j];
3823 int collect_count = element_info[element].collect_count_initial;
3826 if (!IS_CUSTOM_ELEMENT(element))
3829 if (collect_count == 0)
3830 player->inventory_infinite_element = element;
3832 for (k = 0; k < collect_count; k++)
3833 if (player->inventory_size < MAX_INVENTORY_SIZE)
3834 player->inventory_element[player->inventory_size++] = element;
3838 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3839 SnapField(player, 0, 0);
3841 player->LevelSolved = FALSE;
3842 player->GameOver = FALSE;
3844 player->LevelSolved_GameWon = FALSE;
3845 player->LevelSolved_GameEnd = FALSE;
3846 player->LevelSolved_PanelOff = FALSE;
3847 player->LevelSolved_SaveTape = FALSE;
3848 player->LevelSolved_SaveScore = FALSE;
3849 player->LevelSolved_CountingTime = 0;
3850 player->LevelSolved_CountingScore = 0;
3853 network_player_action_received = FALSE;
3855 #if defined(NETWORK_AVALIABLE)
3856 /* initial null action */
3857 if (network_playing)
3858 SendToServer_MovePlayer(MV_NONE);
3867 TimeLeft = level.time;
3870 ScreenMovDir = MV_NONE;
3874 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3876 AllPlayersGone = FALSE;
3878 game.yamyam_content_nr = 0;
3879 game.robot_wheel_active = FALSE;
3880 game.magic_wall_active = FALSE;
3881 game.magic_wall_time_left = 0;
3882 game.light_time_left = 0;
3883 game.timegate_time_left = 0;
3884 game.switchgate_pos = 0;
3885 game.wind_direction = level.wind_direction_initial;
3887 #if !USE_PLAYER_GRAVITY
3888 game.gravity = FALSE;
3889 game.explosions_delayed = TRUE;
3892 game.lenses_time_left = 0;
3893 game.magnify_time_left = 0;
3895 game.ball_state = level.ball_state_initial;
3896 game.ball_content_nr = 0;
3898 game.envelope_active = FALSE;
3900 /* set focus to local player for network games, else to all players */
3901 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3902 game.centered_player_nr_next = game.centered_player_nr;
3903 game.set_centered_player = FALSE;
3905 if (network_playing && tape.recording)
3907 /* store client dependent player focus when recording network games */
3908 tape.centered_player_nr_next = game.centered_player_nr_next;
3909 tape.set_centered_player = TRUE;
3912 for (i = 0; i < NUM_BELTS; i++)
3914 game.belt_dir[i] = MV_NONE;
3915 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3918 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3919 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3921 SCAN_PLAYFIELD(x, y)
3923 Feld[x][y] = level.field[x][y];
3924 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3925 ChangeDelay[x][y] = 0;
3926 ChangePage[x][y] = -1;
3927 #if USE_NEW_CUSTOM_VALUE
3928 CustomValue[x][y] = 0; /* initialized in InitField() */
3930 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3932 WasJustMoving[x][y] = 0;
3933 WasJustFalling[x][y] = 0;
3934 CheckCollision[x][y] = 0;
3935 CheckImpact[x][y] = 0;
3937 Pushed[x][y] = FALSE;
3939 ChangeCount[x][y] = 0;
3940 ChangeEvent[x][y] = -1;
3942 ExplodePhase[x][y] = 0;
3943 ExplodeDelay[x][y] = 0;
3944 ExplodeField[x][y] = EX_TYPE_NONE;
3946 RunnerVisit[x][y] = 0;
3947 PlayerVisit[x][y] = 0;
3950 GfxRandom[x][y] = INIT_GFX_RANDOM();
3951 GfxElement[x][y] = EL_UNDEFINED;
3952 GfxAction[x][y] = ACTION_DEFAULT;
3953 GfxDir[x][y] = MV_NONE;
3954 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3957 SCAN_PLAYFIELD(x, y)
3959 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3961 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3963 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3966 InitField(x, y, TRUE);
3968 ResetGfxAnimation(x, y);
3973 for (i = 0; i < MAX_PLAYERS; i++)
3975 struct PlayerInfo *player = &stored_player[i];
3977 /* set number of special actions for bored and sleeping animation */
3978 player->num_special_action_bored =
3979 get_num_special_action(player->artwork_element,
3980 ACTION_BORING_1, ACTION_BORING_LAST);
3981 player->num_special_action_sleeping =
3982 get_num_special_action(player->artwork_element,
3983 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3986 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3987 emulate_sb ? EMU_SOKOBAN :
3988 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3990 #if USE_NEW_ALL_SLIPPERY
3991 /* initialize type of slippery elements */
3992 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3994 if (!IS_CUSTOM_ELEMENT(i))
3996 /* default: elements slip down either to the left or right randomly */
3997 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3999 /* SP style elements prefer to slip down on the left side */
4000 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4001 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4003 /* BD style elements prefer to slip down on the left side */
4004 if (game.emulation == EMU_BOULDERDASH)
4005 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4010 /* initialize explosion and ignition delay */
4011 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4013 if (!IS_CUSTOM_ELEMENT(i))
4016 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4017 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4018 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4019 int last_phase = (num_phase + 1) * delay;
4020 int half_phase = (num_phase / 2) * delay;
4022 element_info[i].explosion_delay = last_phase - 1;
4023 element_info[i].ignition_delay = half_phase;
4025 if (i == EL_BLACK_ORB)
4026 element_info[i].ignition_delay = 1;
4030 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4031 element_info[i].explosion_delay = 1;
4033 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4034 element_info[i].ignition_delay = 1;
4038 /* correct non-moving belts to start moving left */
4039 for (i = 0; i < NUM_BELTS; i++)
4040 if (game.belt_dir[i] == MV_NONE)
4041 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044 /* check if any connected player was not found in playfield */
4045 for (i = 0; i < MAX_PLAYERS; i++)
4047 struct PlayerInfo *player = &stored_player[i];
4049 if (player->connected && !player->present)
4051 for (j = 0; j < MAX_PLAYERS; j++)
4053 struct PlayerInfo *some_player = &stored_player[j];
4054 int jx = some_player->jx, jy = some_player->jy;
4056 /* assign first free player found that is present in the playfield */
4057 if (some_player->present && !some_player->connected)
4059 player->present = FALSE;
4060 player->active = FALSE;
4062 some_player->present = TRUE;
4063 some_player->active = TRUE;
4066 player->initial_element = some_player->initial_element;
4067 player->artwork_element = some_player->artwork_element;
4069 player->block_last_field = some_player->block_last_field;
4070 player->block_delay_adjustment = some_player->block_delay_adjustment;
4073 StorePlayer[jx][jy] = some_player->element_nr;
4075 some_player->jx = some_player->last_jx = jx;
4076 some_player->jy = some_player->last_jy = jy;
4078 if (local_player == player)
4079 local_player = some_player;
4089 /* check if any connected player was not found in playfield */
4090 for (i = 0; i < MAX_PLAYERS; i++)
4092 struct PlayerInfo *player = &stored_player[i];
4094 if (player->connected && !player->present)
4096 for (j = 0; j < MAX_PLAYERS; j++)
4098 struct PlayerInfo *some_player = &stored_player[j];
4099 int jx = some_player->jx, jy = some_player->jy;
4101 /* assign first free player found that is present in the playfield */
4102 if (some_player->present && !some_player->connected)
4104 player->present = TRUE;
4105 player->active = TRUE;
4107 some_player->present = FALSE;
4108 some_player->active = FALSE;
4110 player->initial_element = some_player->initial_element;
4111 player->artwork_element = some_player->artwork_element;
4113 player->block_last_field = some_player->block_last_field;
4114 player->block_delay_adjustment = some_player->block_delay_adjustment;
4116 StorePlayer[jx][jy] = player->element_nr;
4118 player->jx = player->last_jx = jx;
4119 player->jy = player->last_jy = jy;
4130 /* when playing a tape, eliminate all players who do not participate */
4132 for (i = 0; i < MAX_PLAYERS; i++)
4134 if (stored_player[i].active && !tape.player_participates[i])
4136 struct PlayerInfo *player = &stored_player[i];
4137 int jx = player->jx, jy = player->jy;
4139 player->active = FALSE;
4140 StorePlayer[jx][jy] = 0;
4141 Feld[jx][jy] = EL_EMPTY;
4145 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4147 /* when in single player mode, eliminate all but the first active player */
4149 for (i = 0; i < MAX_PLAYERS; i++)
4151 if (stored_player[i].active)
4153 for (j = i + 1; j < MAX_PLAYERS; j++)
4155 if (stored_player[j].active)
4157 struct PlayerInfo *player = &stored_player[j];
4158 int jx = player->jx, jy = player->jy;
4160 player->active = FALSE;
4161 player->present = FALSE;
4163 StorePlayer[jx][jy] = 0;
4164 Feld[jx][jy] = EL_EMPTY;
4171 /* when recording the game, store which players take part in the game */
4174 for (i = 0; i < MAX_PLAYERS; i++)
4175 if (stored_player[i].active)
4176 tape.player_participates[i] = TRUE;
4181 for (i = 0; i < MAX_PLAYERS; i++)
4183 struct PlayerInfo *player = &stored_player[i];
4185 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4190 if (local_player == player)
4191 printf("Player %d is local player.\n", i+1);
4195 if (BorderElement == EL_EMPTY)
4198 SBX_Right = lev_fieldx - SCR_FIELDX;
4200 SBY_Lower = lev_fieldy - SCR_FIELDY;
4205 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4207 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4210 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4211 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4213 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4214 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4216 /* if local player not found, look for custom element that might create
4217 the player (make some assumptions about the right custom element) */
4218 if (!local_player->present)
4220 int start_x = 0, start_y = 0;
4221 int found_rating = 0;
4222 int found_element = EL_UNDEFINED;
4223 int player_nr = local_player->index_nr;
4225 SCAN_PLAYFIELD(x, y)
4227 int element = Feld[x][y];
4232 if (level.use_start_element[player_nr] &&
4233 level.start_element[player_nr] == element &&
4240 found_element = element;
4243 if (!IS_CUSTOM_ELEMENT(element))
4246 if (CAN_CHANGE(element))
4248 for (i = 0; i < element_info[element].num_change_pages; i++)
4250 /* check for player created from custom element as single target */
4251 content = element_info[element].change_page[i].target_element;
4252 is_player = ELEM_IS_PLAYER(content);
4254 if (is_player && (found_rating < 3 ||
4255 (found_rating == 3 && element < found_element)))
4261 found_element = element;
4266 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4268 /* check for player created from custom element as explosion content */
4269 content = element_info[element].content.e[xx][yy];
4270 is_player = ELEM_IS_PLAYER(content);
4272 if (is_player && (found_rating < 2 ||
4273 (found_rating == 2 && element < found_element)))
4275 start_x = x + xx - 1;
4276 start_y = y + yy - 1;
4279 found_element = element;
4282 if (!CAN_CHANGE(element))
4285 for (i = 0; i < element_info[element].num_change_pages; i++)
4287 /* check for player created from custom element as extended target */
4289 element_info[element].change_page[i].target_content.e[xx][yy];
4291 is_player = ELEM_IS_PLAYER(content);
4293 if (is_player && (found_rating < 1 ||
4294 (found_rating == 1 && element < found_element)))
4296 start_x = x + xx - 1;
4297 start_y = y + yy - 1;
4300 found_element = element;
4306 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4307 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4310 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4311 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4316 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4317 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4318 local_player->jx - MIDPOSX);
4320 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4321 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4322 local_player->jy - MIDPOSY);
4326 /* do not use PLAYING mask for fading out from main screen */
4327 game_status = GAME_MODE_MAIN;
4332 if (!game.restart_level)
4333 CloseDoor(DOOR_CLOSE_1);
4336 if (level_editor_test_game)
4337 FadeSkipNextFadeIn();
4339 FadeSetEnterScreen();
4341 if (level_editor_test_game)
4342 fading = fading_none;
4344 fading = menu.destination;
4348 FadeOut(REDRAW_FIELD);
4351 FadeOut(REDRAW_FIELD);
4355 game_status = GAME_MODE_PLAYING;
4358 /* !!! FIX THIS (START) !!! */
4359 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4361 InitGameEngine_EM();
4363 /* blit playfield from scroll buffer to normal back buffer for fading in */
4364 BlitScreenToBitmap_EM(backbuffer);
4371 /* after drawing the level, correct some elements */
4372 if (game.timegate_time_left == 0)
4373 CloseAllOpenTimegates();
4375 /* blit playfield from scroll buffer to normal back buffer for fading in */
4376 if (setup.soft_scrolling)
4377 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4379 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4381 /* !!! FIX THIS (END) !!! */
4384 FadeIn(REDRAW_FIELD);
4387 FadeIn(REDRAW_FIELD);
4392 if (!game.restart_level)
4394 /* copy default game door content to main double buffer */
4395 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4396 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4399 SetPanelBackground();
4400 SetDrawBackgroundMask(REDRAW_DOOR_1);
4403 UpdateAndDisplayGameControlValues();
4405 UpdateGameDoorValues();
4406 DrawGameDoorValues();
4409 if (!game.restart_level)
4413 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4414 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4415 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4419 /* copy actual game door content to door double buffer for OpenDoor() */
4420 BlitBitmap(drawto, bitmap_db_door,
4421 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4423 OpenDoor(DOOR_OPEN_ALL);
4425 PlaySound(SND_GAME_STARTING);
4427 if (setup.sound_music)
4430 KeyboardAutoRepeatOffUnlessAutoplay();
4434 for (i = 0; i < MAX_PLAYERS; i++)
4435 printf("Player %d %sactive.\n",
4436 i + 1, (stored_player[i].active ? "" : "not "));
4447 game.restart_level = FALSE;
4450 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4452 /* this is used for non-R'n'D game engines to update certain engine values */
4454 /* needed to determine if sounds are played within the visible screen area */
4455 scroll_x = actual_scroll_x;
4456 scroll_y = actual_scroll_y;
4459 void InitMovDir(int x, int y)
4461 int i, element = Feld[x][y];
4462 static int xy[4][2] =
4469 static int direction[3][4] =
4471 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4472 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4473 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4482 Feld[x][y] = EL_BUG;
4483 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4486 case EL_SPACESHIP_RIGHT:
4487 case EL_SPACESHIP_UP:
4488 case EL_SPACESHIP_LEFT:
4489 case EL_SPACESHIP_DOWN:
4490 Feld[x][y] = EL_SPACESHIP;
4491 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4494 case EL_BD_BUTTERFLY_RIGHT:
4495 case EL_BD_BUTTERFLY_UP:
4496 case EL_BD_BUTTERFLY_LEFT:
4497 case EL_BD_BUTTERFLY_DOWN:
4498 Feld[x][y] = EL_BD_BUTTERFLY;
4499 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4502 case EL_BD_FIREFLY_RIGHT:
4503 case EL_BD_FIREFLY_UP:
4504 case EL_BD_FIREFLY_LEFT:
4505 case EL_BD_FIREFLY_DOWN:
4506 Feld[x][y] = EL_BD_FIREFLY;
4507 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4510 case EL_PACMAN_RIGHT:
4512 case EL_PACMAN_LEFT:
4513 case EL_PACMAN_DOWN:
4514 Feld[x][y] = EL_PACMAN;
4515 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4518 case EL_YAMYAM_LEFT:
4519 case EL_YAMYAM_RIGHT:
4521 case EL_YAMYAM_DOWN:
4522 Feld[x][y] = EL_YAMYAM;
4523 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4526 case EL_SP_SNIKSNAK:
4527 MovDir[x][y] = MV_UP;
4530 case EL_SP_ELECTRON:
4531 MovDir[x][y] = MV_LEFT;
4538 Feld[x][y] = EL_MOLE;
4539 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4543 if (IS_CUSTOM_ELEMENT(element))
4545 struct ElementInfo *ei = &element_info[element];
4546 int move_direction_initial = ei->move_direction_initial;
4547 int move_pattern = ei->move_pattern;
4549 if (move_direction_initial == MV_START_PREVIOUS)
4551 if (MovDir[x][y] != MV_NONE)
4554 move_direction_initial = MV_START_AUTOMATIC;
4557 if (move_direction_initial == MV_START_RANDOM)
4558 MovDir[x][y] = 1 << RND(4);
4559 else if (move_direction_initial & MV_ANY_DIRECTION)
4560 MovDir[x][y] = move_direction_initial;
4561 else if (move_pattern == MV_ALL_DIRECTIONS ||
4562 move_pattern == MV_TURNING_LEFT ||
4563 move_pattern == MV_TURNING_RIGHT ||
4564 move_pattern == MV_TURNING_LEFT_RIGHT ||
4565 move_pattern == MV_TURNING_RIGHT_LEFT ||
4566 move_pattern == MV_TURNING_RANDOM)
4567 MovDir[x][y] = 1 << RND(4);
4568 else if (move_pattern == MV_HORIZONTAL)
4569 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4570 else if (move_pattern == MV_VERTICAL)
4571 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4572 else if (move_pattern & MV_ANY_DIRECTION)
4573 MovDir[x][y] = element_info[element].move_pattern;
4574 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4575 move_pattern == MV_ALONG_RIGHT_SIDE)
4577 /* use random direction as default start direction */
4578 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4579 MovDir[x][y] = 1 << RND(4);
4581 for (i = 0; i < NUM_DIRECTIONS; i++)
4583 int x1 = x + xy[i][0];
4584 int y1 = y + xy[i][1];
4586 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4588 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4589 MovDir[x][y] = direction[0][i];
4591 MovDir[x][y] = direction[1][i];
4600 MovDir[x][y] = 1 << RND(4);
4602 if (element != EL_BUG &&
4603 element != EL_SPACESHIP &&
4604 element != EL_BD_BUTTERFLY &&
4605 element != EL_BD_FIREFLY)
4608 for (i = 0; i < NUM_DIRECTIONS; i++)
4610 int x1 = x + xy[i][0];
4611 int y1 = y + xy[i][1];
4613 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4615 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4617 MovDir[x][y] = direction[0][i];
4620 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4621 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4623 MovDir[x][y] = direction[1][i];
4632 GfxDir[x][y] = MovDir[x][y];
4635 void InitAmoebaNr(int x, int y)
4638 int group_nr = AmoebeNachbarNr(x, y);
4642 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4644 if (AmoebaCnt[i] == 0)
4652 AmoebaNr[x][y] = group_nr;
4653 AmoebaCnt[group_nr]++;
4654 AmoebaCnt2[group_nr]++;
4657 static void PlayerWins(struct PlayerInfo *player)
4659 player->LevelSolved = TRUE;
4660 player->GameOver = TRUE;
4662 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4663 level.native_em_level->lev->score : player->score);
4665 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4666 player->LevelSolved_CountingScore = player->score_final;
4671 static int time, time_final;
4672 static int score, score_final;
4673 static int game_over_delay_1 = 0;
4674 static int game_over_delay_2 = 0;
4675 int game_over_delay_value_1 = 50;
4676 int game_over_delay_value_2 = 50;
4678 if (!local_player->LevelSolved_GameWon)
4682 /* do not start end game actions before the player stops moving (to exit) */
4683 if (local_player->MovPos)
4686 local_player->LevelSolved_GameWon = TRUE;
4687 local_player->LevelSolved_SaveTape = tape.recording;
4688 local_player->LevelSolved_SaveScore = !tape.playing;
4690 if (tape.auto_play) /* tape might already be stopped here */
4691 tape.auto_play_level_solved = TRUE;
4697 game_over_delay_1 = game_over_delay_value_1;
4698 game_over_delay_2 = game_over_delay_value_2;
4700 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4701 score = score_final = local_player->score_final;
4706 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4708 else if (level.time == 0 && TimePlayed < 999)
4711 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4714 local_player->score_final = score_final;
4716 if (level_editor_test_game)
4719 score = score_final;
4722 local_player->LevelSolved_CountingTime = time;
4723 local_player->LevelSolved_CountingScore = score;
4725 game_panel_controls[GAME_PANEL_TIME].value = time;
4726 game_panel_controls[GAME_PANEL_SCORE].value = score;
4728 DisplayGameControlValues();
4730 DrawGameValue_Time(time);
4731 DrawGameValue_Score(score);
4735 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4737 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4739 /* close exit door after last player */
4740 if ((AllPlayersGone &&
4741 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4742 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4743 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4744 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4745 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4747 int element = Feld[ExitX][ExitY];
4750 if (element == EL_EM_EXIT_OPEN ||
4751 element == EL_EM_STEEL_EXIT_OPEN)
4758 Feld[ExitX][ExitY] =
4759 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4760 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4761 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4762 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4763 EL_EM_STEEL_EXIT_CLOSING);
4765 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4769 /* player disappears */
4770 DrawLevelField(ExitX, ExitY);
4773 for (i = 0; i < MAX_PLAYERS; i++)
4775 struct PlayerInfo *player = &stored_player[i];
4777 if (player->present)
4779 RemovePlayer(player);
4781 /* player disappears */
4782 DrawLevelField(player->jx, player->jy);
4787 PlaySound(SND_GAME_WINNING);
4790 if (game_over_delay_1 > 0)
4792 game_over_delay_1--;
4797 if (time != time_final)
4799 int time_to_go = ABS(time_final - time);
4800 int time_count_dir = (time < time_final ? +1 : -1);
4801 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4803 time += time_count_steps * time_count_dir;
4804 score += time_count_steps * level.score[SC_TIME_BONUS];
4807 local_player->LevelSolved_CountingTime = time;
4808 local_player->LevelSolved_CountingScore = score;
4810 game_panel_controls[GAME_PANEL_TIME].value = time;
4811 game_panel_controls[GAME_PANEL_SCORE].value = score;
4813 DisplayGameControlValues();
4815 DrawGameValue_Time(time);
4816 DrawGameValue_Score(score);
4819 if (time == time_final)
4820 StopSound(SND_GAME_LEVELTIME_BONUS);
4821 else if (setup.sound_loops)
4822 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4824 PlaySound(SND_GAME_LEVELTIME_BONUS);
4829 local_player->LevelSolved_PanelOff = TRUE;
4831 if (game_over_delay_2 > 0)
4833 game_over_delay_2--;
4846 boolean raise_level = FALSE;
4848 local_player->LevelSolved_GameEnd = TRUE;
4850 CloseDoor(DOOR_CLOSE_1);
4852 if (local_player->LevelSolved_SaveTape)
4859 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4861 SaveTape(tape.level_nr); /* ask to save tape */
4865 if (level_editor_test_game)
4867 game_status = GAME_MODE_MAIN;
4870 DrawAndFadeInMainMenu(REDRAW_FIELD);
4878 if (!local_player->LevelSolved_SaveScore)
4881 FadeOut(REDRAW_FIELD);
4884 game_status = GAME_MODE_MAIN;
4886 DrawAndFadeInMainMenu(REDRAW_FIELD);
4891 if (level_nr == leveldir_current->handicap_level)
4893 leveldir_current->handicap_level++;
4894 SaveLevelSetup_SeriesInfo();
4897 if (level_nr < leveldir_current->last_level)
4898 raise_level = TRUE; /* advance to next level */
4900 if ((hi_pos = NewHiScore()) >= 0)
4902 game_status = GAME_MODE_SCORES;
4904 DrawHallOfFame(hi_pos);
4915 FadeOut(REDRAW_FIELD);
4918 game_status = GAME_MODE_MAIN;
4926 DrawAndFadeInMainMenu(REDRAW_FIELD);
4935 LoadScore(level_nr);
4937 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4938 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4941 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4943 if (local_player->score_final > highscore[k].Score)
4945 /* player has made it to the hall of fame */
4947 if (k < MAX_SCORE_ENTRIES - 1)
4949 int m = MAX_SCORE_ENTRIES - 1;
4952 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4953 if (strEqual(setup.player_name, highscore[l].Name))
4955 if (m == k) /* player's new highscore overwrites his old one */
4959 for (l = m; l > k; l--)
4961 strcpy(highscore[l].Name, highscore[l - 1].Name);
4962 highscore[l].Score = highscore[l - 1].Score;
4969 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4970 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4971 highscore[k].Score = local_player->score_final;
4977 else if (!strncmp(setup.player_name, highscore[k].Name,
4978 MAX_PLAYER_NAME_LEN))
4979 break; /* player already there with a higher score */
4985 SaveScore(level_nr);
4990 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4992 int element = Feld[x][y];
4993 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4994 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4995 int horiz_move = (dx != 0);
4996 int sign = (horiz_move ? dx : dy);
4997 int step = sign * element_info[element].move_stepsize;
4999 /* special values for move stepsize for spring and things on conveyor belt */
5002 if (CAN_FALL(element) &&
5003 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5004 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5005 else if (element == EL_SPRING)
5006 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5012 inline static int getElementMoveStepsize(int x, int y)
5014 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5017 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5019 if (player->GfxAction != action || player->GfxDir != dir)
5022 printf("Player frame reset! (%d => %d, %d => %d)\n",
5023 player->GfxAction, action, player->GfxDir, dir);
5026 player->GfxAction = action;
5027 player->GfxDir = dir;
5029 player->StepFrame = 0;
5033 #if USE_GFX_RESET_GFX_ANIMATION
5034 static void ResetGfxFrame(int x, int y, boolean redraw)
5036 int element = Feld[x][y];
5037 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5038 int last_gfx_frame = GfxFrame[x][y];
5040 if (graphic_info[graphic].anim_global_sync)
5041 GfxFrame[x][y] = FrameCounter;
5042 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5043 GfxFrame[x][y] = CustomValue[x][y];
5044 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5045 GfxFrame[x][y] = element_info[element].collect_score;
5046 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5047 GfxFrame[x][y] = ChangeDelay[x][y];
5049 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5050 DrawLevelGraphicAnimation(x, y, graphic);
5054 static void ResetGfxAnimation(int x, int y)
5056 GfxAction[x][y] = ACTION_DEFAULT;
5057 GfxDir[x][y] = MovDir[x][y];
5060 #if USE_GFX_RESET_GFX_ANIMATION
5061 ResetGfxFrame(x, y, FALSE);
5065 static void ResetRandomAnimationValue(int x, int y)
5067 GfxRandom[x][y] = INIT_GFX_RANDOM();
5070 void InitMovingField(int x, int y, int direction)
5072 int element = Feld[x][y];
5073 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5074 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5077 boolean is_moving_before, is_moving_after;
5079 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5082 /* check if element was/is moving or being moved before/after mode change */
5085 is_moving_before = (WasJustMoving[x][y] != 0);
5087 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5088 is_moving_before = WasJustMoving[x][y];
5091 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5093 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5095 /* reset animation only for moving elements which change direction of moving
5096 or which just started or stopped moving
5097 (else CEs with property "can move" / "not moving" are reset each frame) */
5098 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5100 if (is_moving_before != is_moving_after ||
5101 direction != MovDir[x][y])
5102 ResetGfxAnimation(x, y);
5104 if ((is_moving_before || is_moving_after) && !continues_moving)
5105 ResetGfxAnimation(x, y);
5108 if (!continues_moving)
5109 ResetGfxAnimation(x, y);
5112 MovDir[x][y] = direction;
5113 GfxDir[x][y] = direction;
5115 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5116 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5117 direction == MV_DOWN && CAN_FALL(element) ?
5118 ACTION_FALLING : ACTION_MOVING);
5120 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5121 ACTION_FALLING : ACTION_MOVING);
5124 /* this is needed for CEs with property "can move" / "not moving" */
5126 if (is_moving_after)
5128 if (Feld[newx][newy] == EL_EMPTY)
5129 Feld[newx][newy] = EL_BLOCKED;
5131 MovDir[newx][newy] = MovDir[x][y];
5133 #if USE_NEW_CUSTOM_VALUE
5134 CustomValue[newx][newy] = CustomValue[x][y];
5137 GfxFrame[newx][newy] = GfxFrame[x][y];
5138 GfxRandom[newx][newy] = GfxRandom[x][y];
5139 GfxAction[newx][newy] = GfxAction[x][y];
5140 GfxDir[newx][newy] = GfxDir[x][y];
5144 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5146 int direction = MovDir[x][y];
5147 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5148 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5154 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5156 int oldx = x, oldy = y;
5157 int direction = MovDir[x][y];
5159 if (direction == MV_LEFT)
5161 else if (direction == MV_RIGHT)
5163 else if (direction == MV_UP)
5165 else if (direction == MV_DOWN)
5168 *comes_from_x = oldx;
5169 *comes_from_y = oldy;
5172 int MovingOrBlocked2Element(int x, int y)
5174 int element = Feld[x][y];
5176 if (element == EL_BLOCKED)
5180 Blocked2Moving(x, y, &oldx, &oldy);
5181 return Feld[oldx][oldy];
5187 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5189 /* like MovingOrBlocked2Element(), but if element is moving
5190 and (x,y) is the field the moving element is just leaving,
5191 return EL_BLOCKED instead of the element value */
5192 int element = Feld[x][y];
5194 if (IS_MOVING(x, y))
5196 if (element == EL_BLOCKED)
5200 Blocked2Moving(x, y, &oldx, &oldy);
5201 return Feld[oldx][oldy];
5210 static void RemoveField(int x, int y)
5212 Feld[x][y] = EL_EMPTY;
5218 #if USE_NEW_CUSTOM_VALUE
5219 CustomValue[x][y] = 0;
5223 ChangeDelay[x][y] = 0;
5224 ChangePage[x][y] = -1;
5225 Pushed[x][y] = FALSE;
5228 ExplodeField[x][y] = EX_TYPE_NONE;
5231 GfxElement[x][y] = EL_UNDEFINED;
5232 GfxAction[x][y] = ACTION_DEFAULT;
5233 GfxDir[x][y] = MV_NONE;
5235 /* !!! this would prevent the removed tile from being redrawn !!! */
5236 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5240 void RemoveMovingField(int x, int y)
5242 int oldx = x, oldy = y, newx = x, newy = y;
5243 int element = Feld[x][y];
5244 int next_element = EL_UNDEFINED;
5246 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5249 if (IS_MOVING(x, y))
5251 Moving2Blocked(x, y, &newx, &newy);
5253 if (Feld[newx][newy] != EL_BLOCKED)
5255 /* element is moving, but target field is not free (blocked), but
5256 already occupied by something different (example: acid pool);
5257 in this case, only remove the moving field, but not the target */
5259 RemoveField(oldx, oldy);
5261 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5263 TEST_DrawLevelField(oldx, oldy);
5268 else if (element == EL_BLOCKED)
5270 Blocked2Moving(x, y, &oldx, &oldy);
5271 if (!IS_MOVING(oldx, oldy))
5275 if (element == EL_BLOCKED &&
5276 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5277 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5278 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5279 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5280 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5281 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5282 next_element = get_next_element(Feld[oldx][oldy]);
5284 RemoveField(oldx, oldy);
5285 RemoveField(newx, newy);
5287 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5289 if (next_element != EL_UNDEFINED)
5290 Feld[oldx][oldy] = next_element;
5292 TEST_DrawLevelField(oldx, oldy);
5293 TEST_DrawLevelField(newx, newy);
5296 void DrawDynamite(int x, int y)
5298 int sx = SCREENX(x), sy = SCREENY(y);
5299 int graphic = el2img(Feld[x][y]);
5302 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5305 if (IS_WALKABLE_INSIDE(Back[x][y]))
5309 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5310 else if (Store[x][y])
5311 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5313 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5315 if (Back[x][y] || Store[x][y])
5316 DrawGraphicThruMask(sx, sy, graphic, frame);
5318 DrawGraphic(sx, sy, graphic, frame);
5321 void CheckDynamite(int x, int y)
5323 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5327 if (MovDelay[x][y] != 0)
5330 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5336 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5341 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5343 boolean num_checked_players = 0;
5346 for (i = 0; i < MAX_PLAYERS; i++)
5348 if (stored_player[i].active)
5350 int sx = stored_player[i].jx;
5351 int sy = stored_player[i].jy;
5353 if (num_checked_players == 0)
5360 *sx1 = MIN(*sx1, sx);
5361 *sy1 = MIN(*sy1, sy);
5362 *sx2 = MAX(*sx2, sx);
5363 *sy2 = MAX(*sy2, sy);
5366 num_checked_players++;
5371 static boolean checkIfAllPlayersFitToScreen_RND()
5373 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5375 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5377 return (sx2 - sx1 < SCR_FIELDX &&
5378 sy2 - sy1 < SCR_FIELDY);
5381 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5383 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5385 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5387 *sx = (sx1 + sx2) / 2;
5388 *sy = (sy1 + sy2) / 2;
5391 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5392 boolean center_screen, boolean quick_relocation)
5394 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5395 boolean no_delay = (tape.warp_forward);
5396 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5397 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5399 if (quick_relocation)
5401 int offset = game.scroll_delay_value;
5403 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5405 if (!level.shifted_relocation || center_screen)
5407 /* quick relocation (without scrolling), with centering of screen */
5409 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5410 x > SBX_Right + MIDPOSX ? SBX_Right :
5413 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5414 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5419 /* quick relocation (without scrolling), but do not center screen */
5421 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5422 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5425 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5426 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5429 int offset_x = x + (scroll_x - center_scroll_x);
5430 int offset_y = y + (scroll_y - center_scroll_y);
5432 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5433 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5434 offset_x - MIDPOSX);
5436 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5437 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5438 offset_y - MIDPOSY);
5443 /* quick relocation (without scrolling), inside visible screen area */
5445 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5446 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5447 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5449 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5450 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5451 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5453 /* don't scroll over playfield boundaries */
5454 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5455 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5457 /* don't scroll over playfield boundaries */
5458 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5459 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5462 RedrawPlayfield(TRUE, 0,0,0,0);
5467 int scroll_xx, scroll_yy;
5469 if (!level.shifted_relocation || center_screen)
5471 /* visible relocation (with scrolling), with centering of screen */
5473 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5474 x > SBX_Right + MIDPOSX ? SBX_Right :
5477 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5478 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5483 /* visible relocation (with scrolling), but do not center screen */
5485 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5486 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5489 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5490 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5493 int offset_x = x + (scroll_x - center_scroll_x);
5494 int offset_y = y + (scroll_y - center_scroll_y);
5496 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5497 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5498 offset_x - MIDPOSX);
5500 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5501 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5502 offset_y - MIDPOSY);
5507 /* visible relocation (with scrolling), with centering of screen */
5509 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5510 x > SBX_Right + MIDPOSX ? SBX_Right :
5513 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5514 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5518 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5520 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5523 int fx = FX, fy = FY;
5525 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5526 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5528 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5534 fx += dx * TILEX / 2;
5535 fy += dy * TILEY / 2;
5537 ScrollLevel(dx, dy);
5540 /* scroll in two steps of half tile size to make things smoother */
5541 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5543 Delay(wait_delay_value);
5545 /* scroll second step to align at full tile size */
5547 Delay(wait_delay_value);
5552 Delay(wait_delay_value);
5556 void RelocatePlayer(int jx, int jy, int el_player_raw)
5558 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5559 int player_nr = GET_PLAYER_NR(el_player);
5560 struct PlayerInfo *player = &stored_player[player_nr];
5561 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5562 boolean no_delay = (tape.warp_forward);
5563 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5564 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5565 int old_jx = player->jx;
5566 int old_jy = player->jy;
5567 int old_element = Feld[old_jx][old_jy];
5568 int element = Feld[jx][jy];
5569 boolean player_relocated = (old_jx != jx || old_jy != jy);
5571 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5572 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5573 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5574 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5575 int leave_side_horiz = move_dir_horiz;
5576 int leave_side_vert = move_dir_vert;
5577 int enter_side = enter_side_horiz | enter_side_vert;
5578 int leave_side = leave_side_horiz | leave_side_vert;
5580 if (player->GameOver) /* do not reanimate dead player */
5583 if (!player_relocated) /* no need to relocate the player */
5586 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5588 RemoveField(jx, jy); /* temporarily remove newly placed player */
5589 DrawLevelField(jx, jy);
5592 if (player->present)
5594 while (player->MovPos)
5596 ScrollPlayer(player, SCROLL_GO_ON);
5597 ScrollScreen(NULL, SCROLL_GO_ON);
5599 AdvanceFrameAndPlayerCounters(player->index_nr);
5604 Delay(wait_delay_value);
5607 DrawPlayer(player); /* needed here only to cleanup last field */
5608 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5610 player->is_moving = FALSE;
5613 if (IS_CUSTOM_ELEMENT(old_element))
5614 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5616 player->index_bit, leave_side);
5618 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5620 player->index_bit, leave_side);
5622 Feld[jx][jy] = el_player;
5623 InitPlayerField(jx, jy, el_player, TRUE);
5625 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5627 Feld[jx][jy] = element;
5628 InitField(jx, jy, FALSE);
5631 /* only visually relocate centered player */
5632 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5633 FALSE, level.instant_relocation);
5635 TestIfPlayerTouchesBadThing(jx, jy);
5636 TestIfPlayerTouchesCustomElement(jx, jy);
5638 if (IS_CUSTOM_ELEMENT(element))
5639 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5640 player->index_bit, enter_side);
5642 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5643 player->index_bit, enter_side);
5646 void Explode(int ex, int ey, int phase, int mode)
5652 /* !!! eliminate this variable !!! */
5653 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5655 if (game.explosions_delayed)
5657 ExplodeField[ex][ey] = mode;
5661 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5663 int center_element = Feld[ex][ey];
5664 int artwork_element, explosion_element; /* set these values later */
5667 /* --- This is only really needed (and now handled) in "Impact()". --- */
5668 /* do not explode moving elements that left the explode field in time */
5669 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5670 center_element == EL_EMPTY &&
5671 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5676 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5677 if (mode == EX_TYPE_NORMAL ||
5678 mode == EX_TYPE_CENTER ||
5679 mode == EX_TYPE_CROSS)
5680 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5683 /* remove things displayed in background while burning dynamite */
5684 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5687 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5689 /* put moving element to center field (and let it explode there) */
5690 center_element = MovingOrBlocked2Element(ex, ey);
5691 RemoveMovingField(ex, ey);
5692 Feld[ex][ey] = center_element;
5695 /* now "center_element" is finally determined -- set related values now */
5696 artwork_element = center_element; /* for custom player artwork */
5697 explosion_element = center_element; /* for custom player artwork */
5699 if (IS_PLAYER(ex, ey))
5701 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5703 artwork_element = stored_player[player_nr].artwork_element;
5705 if (level.use_explosion_element[player_nr])
5707 explosion_element = level.explosion_element[player_nr];
5708 artwork_element = explosion_element;
5713 if (mode == EX_TYPE_NORMAL ||
5714 mode == EX_TYPE_CENTER ||
5715 mode == EX_TYPE_CROSS)
5716 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5719 last_phase = element_info[explosion_element].explosion_delay + 1;
5721 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5723 int xx = x - ex + 1;
5724 int yy = y - ey + 1;
5727 if (!IN_LEV_FIELD(x, y) ||
5728 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5729 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5732 element = Feld[x][y];
5734 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5736 element = MovingOrBlocked2Element(x, y);
5738 if (!IS_EXPLOSION_PROOF(element))
5739 RemoveMovingField(x, y);
5742 /* indestructible elements can only explode in center (but not flames) */
5743 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5744 mode == EX_TYPE_BORDER)) ||
5745 element == EL_FLAMES)
5748 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5749 behaviour, for example when touching a yamyam that explodes to rocks
5750 with active deadly shield, a rock is created under the player !!! */
5751 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5753 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5754 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5755 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5757 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5760 if (IS_ACTIVE_BOMB(element))
5762 /* re-activate things under the bomb like gate or penguin */
5763 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5770 /* save walkable background elements while explosion on same tile */
5771 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5772 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5773 Back[x][y] = element;
5775 /* ignite explodable elements reached by other explosion */
5776 if (element == EL_EXPLOSION)
5777 element = Store2[x][y];
5779 if (AmoebaNr[x][y] &&
5780 (element == EL_AMOEBA_FULL ||
5781 element == EL_BD_AMOEBA ||
5782 element == EL_AMOEBA_GROWING))
5784 AmoebaCnt[AmoebaNr[x][y]]--;
5785 AmoebaCnt2[AmoebaNr[x][y]]--;
5790 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5792 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5794 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5796 if (PLAYERINFO(ex, ey)->use_murphy)
5797 Store[x][y] = EL_EMPTY;
5800 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5801 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5802 else if (ELEM_IS_PLAYER(center_element))
5803 Store[x][y] = EL_EMPTY;
5804 else if (center_element == EL_YAMYAM)
5805 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5806 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5807 Store[x][y] = element_info[center_element].content.e[xx][yy];
5809 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5810 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5811 otherwise) -- FIX THIS !!! */
5812 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5813 Store[x][y] = element_info[element].content.e[1][1];
5815 else if (!CAN_EXPLODE(element))
5816 Store[x][y] = element_info[element].content.e[1][1];
5819 Store[x][y] = EL_EMPTY;
5821 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5822 center_element == EL_AMOEBA_TO_DIAMOND)
5823 Store2[x][y] = element;
5825 Feld[x][y] = EL_EXPLOSION;
5826 GfxElement[x][y] = artwork_element;
5828 ExplodePhase[x][y] = 1;
5829 ExplodeDelay[x][y] = last_phase;
5834 if (center_element == EL_YAMYAM)
5835 game.yamyam_content_nr =
5836 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5848 GfxFrame[x][y] = 0; /* restart explosion animation */
5850 last_phase = ExplodeDelay[x][y];
5852 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5856 /* activate this even in non-DEBUG version until cause for crash in
5857 getGraphicAnimationFrame() (see below) is found and eliminated */
5863 /* this can happen if the player leaves an explosion just in time */
5864 if (GfxElement[x][y] == EL_UNDEFINED)
5865 GfxElement[x][y] = EL_EMPTY;
5867 if (GfxElement[x][y] == EL_UNDEFINED)
5870 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5871 printf("Explode(): This should never happen!\n");
5874 GfxElement[x][y] = EL_EMPTY;
5880 border_element = Store2[x][y];
5881 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5882 border_element = StorePlayer[x][y];
5884 if (phase == element_info[border_element].ignition_delay ||
5885 phase == last_phase)
5887 boolean border_explosion = FALSE;
5889 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5890 !PLAYER_EXPLOSION_PROTECTED(x, y))
5892 KillPlayerUnlessExplosionProtected(x, y);
5893 border_explosion = TRUE;
5895 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5897 Feld[x][y] = Store2[x][y];
5900 border_explosion = TRUE;
5902 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5904 AmoebeUmwandeln(x, y);
5906 border_explosion = TRUE;
5909 /* if an element just explodes due to another explosion (chain-reaction),
5910 do not immediately end the new explosion when it was the last frame of
5911 the explosion (as it would be done in the following "if"-statement!) */
5912 if (border_explosion && phase == last_phase)
5916 if (phase == last_phase)
5920 element = Feld[x][y] = Store[x][y];
5921 Store[x][y] = Store2[x][y] = 0;
5922 GfxElement[x][y] = EL_UNDEFINED;
5924 /* player can escape from explosions and might therefore be still alive */
5925 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5926 element <= EL_PLAYER_IS_EXPLODING_4)
5928 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5929 int explosion_element = EL_PLAYER_1 + player_nr;
5930 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5931 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5933 if (level.use_explosion_element[player_nr])
5934 explosion_element = level.explosion_element[player_nr];
5936 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5937 element_info[explosion_element].content.e[xx][yy]);
5940 /* restore probably existing indestructible background element */
5941 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5942 element = Feld[x][y] = Back[x][y];
5945 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5946 GfxDir[x][y] = MV_NONE;
5947 ChangeDelay[x][y] = 0;
5948 ChangePage[x][y] = -1;
5950 #if USE_NEW_CUSTOM_VALUE
5951 CustomValue[x][y] = 0;
5954 InitField_WithBug2(x, y, FALSE);
5956 TEST_DrawLevelField(x, y);
5958 TestIfElementTouchesCustomElement(x, y);
5960 if (GFX_CRUMBLED(element))
5961 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
5963 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5964 StorePlayer[x][y] = 0;
5966 if (ELEM_IS_PLAYER(element))
5967 RelocatePlayer(x, y, element);
5969 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5971 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5972 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5975 TEST_DrawLevelFieldCrumbledSand(x, y);
5977 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5979 DrawLevelElement(x, y, Back[x][y]);
5980 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5982 else if (IS_WALKABLE_UNDER(Back[x][y]))
5984 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5985 DrawLevelElementThruMask(x, y, Back[x][y]);
5987 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5988 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5992 void DynaExplode(int ex, int ey)
5995 int dynabomb_element = Feld[ex][ey];
5996 int dynabomb_size = 1;
5997 boolean dynabomb_xl = FALSE;
5998 struct PlayerInfo *player;
5999 static int xy[4][2] =
6007 if (IS_ACTIVE_BOMB(dynabomb_element))
6009 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6010 dynabomb_size = player->dynabomb_size;
6011 dynabomb_xl = player->dynabomb_xl;
6012 player->dynabombs_left++;
6015 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6017 for (i = 0; i < NUM_DIRECTIONS; i++)
6019 for (j = 1; j <= dynabomb_size; j++)
6021 int x = ex + j * xy[i][0];
6022 int y = ey + j * xy[i][1];
6025 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6028 element = Feld[x][y];
6030 /* do not restart explosions of fields with active bombs */
6031 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6034 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6036 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6037 !IS_DIGGABLE(element) && !dynabomb_xl)
6043 void Bang(int x, int y)
6045 int element = MovingOrBlocked2Element(x, y);
6046 int explosion_type = EX_TYPE_NORMAL;
6048 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6050 struct PlayerInfo *player = PLAYERINFO(x, y);
6052 #if USE_FIX_CE_ACTION_WITH_PLAYER
6053 element = Feld[x][y] = player->initial_element;
6055 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6056 player->element_nr);
6059 if (level.use_explosion_element[player->index_nr])
6061 int explosion_element = level.explosion_element[player->index_nr];
6063 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6064 explosion_type = EX_TYPE_CROSS;
6065 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6066 explosion_type = EX_TYPE_CENTER;
6074 case EL_BD_BUTTERFLY:
6077 case EL_DARK_YAMYAM:
6081 RaiseScoreElement(element);
6084 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6085 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6086 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6087 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6088 case EL_DYNABOMB_INCREASE_NUMBER:
6089 case EL_DYNABOMB_INCREASE_SIZE:
6090 case EL_DYNABOMB_INCREASE_POWER:
6091 explosion_type = EX_TYPE_DYNA;
6094 case EL_DC_LANDMINE:
6096 case EL_EM_EXIT_OPEN:
6097 case EL_EM_STEEL_EXIT_OPEN:
6099 explosion_type = EX_TYPE_CENTER;
6104 case EL_LAMP_ACTIVE:
6105 case EL_AMOEBA_TO_DIAMOND:
6106 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6107 explosion_type = EX_TYPE_CENTER;
6111 if (element_info[element].explosion_type == EXPLODES_CROSS)
6112 explosion_type = EX_TYPE_CROSS;
6113 else if (element_info[element].explosion_type == EXPLODES_1X1)
6114 explosion_type = EX_TYPE_CENTER;
6118 if (explosion_type == EX_TYPE_DYNA)
6121 Explode(x, y, EX_PHASE_START, explosion_type);
6123 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6126 void SplashAcid(int x, int y)
6128 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6129 (!IN_LEV_FIELD(x - 1, y - 2) ||
6130 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6131 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6133 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6134 (!IN_LEV_FIELD(x + 1, y - 2) ||
6135 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6136 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6138 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6141 static void InitBeltMovement()
6143 static int belt_base_element[4] =
6145 EL_CONVEYOR_BELT_1_LEFT,
6146 EL_CONVEYOR_BELT_2_LEFT,
6147 EL_CONVEYOR_BELT_3_LEFT,
6148 EL_CONVEYOR_BELT_4_LEFT
6150 static int belt_base_active_element[4] =
6152 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6153 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6154 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6155 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6160 /* set frame order for belt animation graphic according to belt direction */
6161 for (i = 0; i < NUM_BELTS; i++)
6165 for (j = 0; j < NUM_BELT_PARTS; j++)
6167 int element = belt_base_active_element[belt_nr] + j;
6168 int graphic_1 = el2img(element);
6169 int graphic_2 = el2panelimg(element);
6171 if (game.belt_dir[i] == MV_LEFT)
6173 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6174 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6178 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6179 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6184 SCAN_PLAYFIELD(x, y)
6186 int element = Feld[x][y];
6188 for (i = 0; i < NUM_BELTS; i++)
6190 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6192 int e_belt_nr = getBeltNrFromBeltElement(element);
6195 if (e_belt_nr == belt_nr)
6197 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6199 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6206 static void ToggleBeltSwitch(int x, int y)
6208 static int belt_base_element[4] =
6210 EL_CONVEYOR_BELT_1_LEFT,
6211 EL_CONVEYOR_BELT_2_LEFT,
6212 EL_CONVEYOR_BELT_3_LEFT,
6213 EL_CONVEYOR_BELT_4_LEFT
6215 static int belt_base_active_element[4] =
6217 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6218 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6219 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6220 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6222 static int belt_base_switch_element[4] =
6224 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6225 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6226 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6227 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6229 static int belt_move_dir[4] =
6237 int element = Feld[x][y];
6238 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6239 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6240 int belt_dir = belt_move_dir[belt_dir_nr];
6243 if (!IS_BELT_SWITCH(element))
6246 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6247 game.belt_dir[belt_nr] = belt_dir;
6249 if (belt_dir_nr == 3)
6252 /* set frame order for belt animation graphic according to belt direction */
6253 for (i = 0; i < NUM_BELT_PARTS; i++)
6255 int element = belt_base_active_element[belt_nr] + i;
6256 int graphic_1 = el2img(element);
6257 int graphic_2 = el2panelimg(element);
6259 if (belt_dir == MV_LEFT)
6261 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6262 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6266 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6267 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6271 SCAN_PLAYFIELD(xx, yy)
6273 int element = Feld[xx][yy];
6275 if (IS_BELT_SWITCH(element))
6277 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6279 if (e_belt_nr == belt_nr)
6281 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6282 TEST_DrawLevelField(xx, yy);
6285 else if (IS_BELT(element) && belt_dir != MV_NONE)
6287 int e_belt_nr = getBeltNrFromBeltElement(element);
6289 if (e_belt_nr == belt_nr)
6291 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6293 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6294 TEST_DrawLevelField(xx, yy);
6297 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6299 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6301 if (e_belt_nr == belt_nr)
6303 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6305 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6306 TEST_DrawLevelField(xx, yy);
6312 static void ToggleSwitchgateSwitch(int x, int y)
6316 game.switchgate_pos = !game.switchgate_pos;
6318 SCAN_PLAYFIELD(xx, yy)
6320 int element = Feld[xx][yy];
6322 #if !USE_BOTH_SWITCHGATE_SWITCHES
6323 if (element == EL_SWITCHGATE_SWITCH_UP ||
6324 element == EL_SWITCHGATE_SWITCH_DOWN)
6326 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6327 TEST_DrawLevelField(xx, yy);
6329 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6330 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6332 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6333 TEST_DrawLevelField(xx, yy);
6336 if (element == EL_SWITCHGATE_SWITCH_UP)
6338 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6339 TEST_DrawLevelField(xx, yy);
6341 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6343 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6344 TEST_DrawLevelField(xx, yy);
6346 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6348 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6349 TEST_DrawLevelField(xx, yy);
6351 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6353 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6354 TEST_DrawLevelField(xx, yy);
6357 else if (element == EL_SWITCHGATE_OPEN ||
6358 element == EL_SWITCHGATE_OPENING)
6360 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6362 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6364 else if (element == EL_SWITCHGATE_CLOSED ||
6365 element == EL_SWITCHGATE_CLOSING)
6367 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6369 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6374 static int getInvisibleActiveFromInvisibleElement(int element)
6376 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6377 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6378 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6382 static int getInvisibleFromInvisibleActiveElement(int element)
6384 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6385 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6386 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6390 static void RedrawAllLightSwitchesAndInvisibleElements()
6394 SCAN_PLAYFIELD(x, y)
6396 int element = Feld[x][y];
6398 if (element == EL_LIGHT_SWITCH &&
6399 game.light_time_left > 0)
6401 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6402 TEST_DrawLevelField(x, y);
6404 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6405 game.light_time_left == 0)
6407 Feld[x][y] = EL_LIGHT_SWITCH;
6408 TEST_DrawLevelField(x, y);
6410 else if (element == EL_EMC_DRIPPER &&
6411 game.light_time_left > 0)
6413 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6414 TEST_DrawLevelField(x, y);
6416 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6417 game.light_time_left == 0)
6419 Feld[x][y] = EL_EMC_DRIPPER;
6420 TEST_DrawLevelField(x, y);
6422 else if (element == EL_INVISIBLE_STEELWALL ||
6423 element == EL_INVISIBLE_WALL ||
6424 element == EL_INVISIBLE_SAND)
6426 if (game.light_time_left > 0)
6427 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6429 TEST_DrawLevelField(x, y);
6431 /* uncrumble neighbour fields, if needed */
6432 if (element == EL_INVISIBLE_SAND)
6433 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6435 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6436 element == EL_INVISIBLE_WALL_ACTIVE ||
6437 element == EL_INVISIBLE_SAND_ACTIVE)
6439 if (game.light_time_left == 0)
6440 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6442 TEST_DrawLevelField(x, y);
6444 /* re-crumble neighbour fields, if needed */
6445 if (element == EL_INVISIBLE_SAND)
6446 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6451 static void RedrawAllInvisibleElementsForLenses()
6455 SCAN_PLAYFIELD(x, y)
6457 int element = Feld[x][y];
6459 if (element == EL_EMC_DRIPPER &&
6460 game.lenses_time_left > 0)
6462 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6463 TEST_DrawLevelField(x, y);
6465 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6466 game.lenses_time_left == 0)
6468 Feld[x][y] = EL_EMC_DRIPPER;
6469 TEST_DrawLevelField(x, y);
6471 else if (element == EL_INVISIBLE_STEELWALL ||
6472 element == EL_INVISIBLE_WALL ||
6473 element == EL_INVISIBLE_SAND)
6475 if (game.lenses_time_left > 0)
6476 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6478 TEST_DrawLevelField(x, y);
6480 /* uncrumble neighbour fields, if needed */
6481 if (element == EL_INVISIBLE_SAND)
6482 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6484 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6485 element == EL_INVISIBLE_WALL_ACTIVE ||
6486 element == EL_INVISIBLE_SAND_ACTIVE)
6488 if (game.lenses_time_left == 0)
6489 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6491 TEST_DrawLevelField(x, y);
6493 /* re-crumble neighbour fields, if needed */
6494 if (element == EL_INVISIBLE_SAND)
6495 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6500 static void RedrawAllInvisibleElementsForMagnifier()
6504 SCAN_PLAYFIELD(x, y)
6506 int element = Feld[x][y];
6508 if (element == EL_EMC_FAKE_GRASS &&
6509 game.magnify_time_left > 0)
6511 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6512 TEST_DrawLevelField(x, y);
6514 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6515 game.magnify_time_left == 0)
6517 Feld[x][y] = EL_EMC_FAKE_GRASS;
6518 TEST_DrawLevelField(x, y);
6520 else if (IS_GATE_GRAY(element) &&
6521 game.magnify_time_left > 0)
6523 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6524 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6525 IS_EM_GATE_GRAY(element) ?
6526 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6527 IS_EMC_GATE_GRAY(element) ?
6528 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6529 IS_DC_GATE_GRAY(element) ?
6530 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6532 TEST_DrawLevelField(x, y);
6534 else if (IS_GATE_GRAY_ACTIVE(element) &&
6535 game.magnify_time_left == 0)
6537 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6538 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6539 IS_EM_GATE_GRAY_ACTIVE(element) ?
6540 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6541 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6542 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6543 IS_DC_GATE_GRAY_ACTIVE(element) ?
6544 EL_DC_GATE_WHITE_GRAY :
6546 TEST_DrawLevelField(x, y);
6551 static void ToggleLightSwitch(int x, int y)
6553 int element = Feld[x][y];
6555 game.light_time_left =
6556 (element == EL_LIGHT_SWITCH ?
6557 level.time_light * FRAMES_PER_SECOND : 0);
6559 RedrawAllLightSwitchesAndInvisibleElements();
6562 static void ActivateTimegateSwitch(int x, int y)
6566 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6568 SCAN_PLAYFIELD(xx, yy)
6570 int element = Feld[xx][yy];
6572 if (element == EL_TIMEGATE_CLOSED ||
6573 element == EL_TIMEGATE_CLOSING)
6575 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6576 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6580 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6582 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6583 TEST_DrawLevelField(xx, yy);
6590 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6591 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6593 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6597 void Impact(int x, int y)
6599 boolean last_line = (y == lev_fieldy - 1);
6600 boolean object_hit = FALSE;
6601 boolean impact = (last_line || object_hit);
6602 int element = Feld[x][y];
6603 int smashed = EL_STEELWALL;
6605 if (!last_line) /* check if element below was hit */
6607 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6610 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6611 MovDir[x][y + 1] != MV_DOWN ||
6612 MovPos[x][y + 1] <= TILEY / 2));
6614 /* do not smash moving elements that left the smashed field in time */
6615 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6616 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6619 #if USE_QUICKSAND_IMPACT_BUGFIX
6620 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6622 RemoveMovingField(x, y + 1);
6623 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6624 Feld[x][y + 2] = EL_ROCK;
6625 TEST_DrawLevelField(x, y + 2);
6630 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6632 RemoveMovingField(x, y + 1);
6633 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6634 Feld[x][y + 2] = EL_ROCK;
6635 TEST_DrawLevelField(x, y + 2);
6642 smashed = MovingOrBlocked2Element(x, y + 1);
6644 impact = (last_line || object_hit);
6647 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6649 SplashAcid(x, y + 1);
6653 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6654 /* only reset graphic animation if graphic really changes after impact */
6656 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6658 ResetGfxAnimation(x, y);
6659 TEST_DrawLevelField(x, y);
6662 if (impact && CAN_EXPLODE_IMPACT(element))
6667 else if (impact && element == EL_PEARL &&
6668 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6670 ResetGfxAnimation(x, y);
6672 Feld[x][y] = EL_PEARL_BREAKING;
6673 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6676 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6678 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6683 if (impact && element == EL_AMOEBA_DROP)
6685 if (object_hit && IS_PLAYER(x, y + 1))
6686 KillPlayerUnlessEnemyProtected(x, y + 1);
6687 else if (object_hit && smashed == EL_PENGUIN)
6691 Feld[x][y] = EL_AMOEBA_GROWING;
6692 Store[x][y] = EL_AMOEBA_WET;
6694 ResetRandomAnimationValue(x, y);
6699 if (object_hit) /* check which object was hit */
6701 if ((CAN_PASS_MAGIC_WALL(element) &&
6702 (smashed == EL_MAGIC_WALL ||
6703 smashed == EL_BD_MAGIC_WALL)) ||
6704 (CAN_PASS_DC_MAGIC_WALL(element) &&
6705 smashed == EL_DC_MAGIC_WALL))
6708 int activated_magic_wall =
6709 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6710 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6711 EL_DC_MAGIC_WALL_ACTIVE);
6713 /* activate magic wall / mill */
6714 SCAN_PLAYFIELD(xx, yy)
6716 if (Feld[xx][yy] == smashed)
6717 Feld[xx][yy] = activated_magic_wall;
6720 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6721 game.magic_wall_active = TRUE;
6723 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6724 SND_MAGIC_WALL_ACTIVATING :
6725 smashed == EL_BD_MAGIC_WALL ?
6726 SND_BD_MAGIC_WALL_ACTIVATING :
6727 SND_DC_MAGIC_WALL_ACTIVATING));
6730 if (IS_PLAYER(x, y + 1))
6732 if (CAN_SMASH_PLAYER(element))
6734 KillPlayerUnlessEnemyProtected(x, y + 1);
6738 else if (smashed == EL_PENGUIN)
6740 if (CAN_SMASH_PLAYER(element))
6746 else if (element == EL_BD_DIAMOND)
6748 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6754 else if (((element == EL_SP_INFOTRON ||
6755 element == EL_SP_ZONK) &&
6756 (smashed == EL_SP_SNIKSNAK ||
6757 smashed == EL_SP_ELECTRON ||
6758 smashed == EL_SP_DISK_ORANGE)) ||
6759 (element == EL_SP_INFOTRON &&
6760 smashed == EL_SP_DISK_YELLOW))
6765 else if (CAN_SMASH_EVERYTHING(element))
6767 if (IS_CLASSIC_ENEMY(smashed) ||
6768 CAN_EXPLODE_SMASHED(smashed))
6773 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6775 if (smashed == EL_LAMP ||
6776 smashed == EL_LAMP_ACTIVE)
6781 else if (smashed == EL_NUT)
6783 Feld[x][y + 1] = EL_NUT_BREAKING;
6784 PlayLevelSound(x, y, SND_NUT_BREAKING);
6785 RaiseScoreElement(EL_NUT);
6788 else if (smashed == EL_PEARL)
6790 ResetGfxAnimation(x, y);
6792 Feld[x][y + 1] = EL_PEARL_BREAKING;
6793 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6796 else if (smashed == EL_DIAMOND)
6798 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6799 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6802 else if (IS_BELT_SWITCH(smashed))
6804 ToggleBeltSwitch(x, y + 1);
6806 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6807 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6808 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6809 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6811 ToggleSwitchgateSwitch(x, y + 1);
6813 else if (smashed == EL_LIGHT_SWITCH ||
6814 smashed == EL_LIGHT_SWITCH_ACTIVE)
6816 ToggleLightSwitch(x, y + 1);
6821 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6824 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6826 CheckElementChangeBySide(x, y + 1, smashed, element,
6827 CE_SWITCHED, CH_SIDE_TOP);
6828 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6834 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6839 /* play sound of magic wall / mill */
6841 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6842 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6843 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6845 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6846 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6847 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6848 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6849 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6850 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6855 /* play sound of object that hits the ground */
6856 if (last_line || object_hit)
6857 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6860 inline static void TurnRoundExt(int x, int y)
6872 { 0, 0 }, { 0, 0 }, { 0, 0 },
6877 int left, right, back;
6881 { MV_DOWN, MV_UP, MV_RIGHT },
6882 { MV_UP, MV_DOWN, MV_LEFT },
6884 { MV_LEFT, MV_RIGHT, MV_DOWN },
6888 { MV_RIGHT, MV_LEFT, MV_UP }
6891 int element = Feld[x][y];
6892 int move_pattern = element_info[element].move_pattern;
6894 int old_move_dir = MovDir[x][y];
6895 int left_dir = turn[old_move_dir].left;
6896 int right_dir = turn[old_move_dir].right;
6897 int back_dir = turn[old_move_dir].back;
6899 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6900 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6901 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6902 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6904 int left_x = x + left_dx, left_y = y + left_dy;
6905 int right_x = x + right_dx, right_y = y + right_dy;
6906 int move_x = x + move_dx, move_y = y + move_dy;
6910 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6912 TestIfBadThingTouchesOtherBadThing(x, y);
6914 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6915 MovDir[x][y] = right_dir;
6916 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6917 MovDir[x][y] = left_dir;
6919 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6921 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6924 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6926 TestIfBadThingTouchesOtherBadThing(x, y);
6928 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6929 MovDir[x][y] = left_dir;
6930 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6931 MovDir[x][y] = right_dir;
6933 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6935 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6938 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6940 TestIfBadThingTouchesOtherBadThing(x, y);
6942 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6943 MovDir[x][y] = left_dir;
6944 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6945 MovDir[x][y] = right_dir;
6947 if (MovDir[x][y] != old_move_dir)
6950 else if (element == EL_YAMYAM)
6952 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6953 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6955 if (can_turn_left && can_turn_right)
6956 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6957 else if (can_turn_left)
6958 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6959 else if (can_turn_right)
6960 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6962 MovDir[x][y] = back_dir;
6964 MovDelay[x][y] = 16 + 16 * RND(3);
6966 else if (element == EL_DARK_YAMYAM)
6968 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6970 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6973 if (can_turn_left && can_turn_right)
6974 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6975 else if (can_turn_left)
6976 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6977 else if (can_turn_right)
6978 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6980 MovDir[x][y] = back_dir;
6982 MovDelay[x][y] = 16 + 16 * RND(3);
6984 else if (element == EL_PACMAN)
6986 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6987 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6989 if (can_turn_left && can_turn_right)
6990 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6991 else if (can_turn_left)
6992 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6993 else if (can_turn_right)
6994 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6996 MovDir[x][y] = back_dir;
6998 MovDelay[x][y] = 6 + RND(40);
7000 else if (element == EL_PIG)
7002 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7003 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7004 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7005 boolean should_turn_left, should_turn_right, should_move_on;
7007 int rnd = RND(rnd_value);
7009 should_turn_left = (can_turn_left &&
7011 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7012 y + back_dy + left_dy)));
7013 should_turn_right = (can_turn_right &&
7015 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7016 y + back_dy + right_dy)));
7017 should_move_on = (can_move_on &&
7020 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7021 y + move_dy + left_dy) ||
7022 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7023 y + move_dy + right_dy)));
7025 if (should_turn_left || should_turn_right || should_move_on)
7027 if (should_turn_left && should_turn_right && should_move_on)
7028 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7029 rnd < 2 * rnd_value / 3 ? right_dir :
7031 else if (should_turn_left && should_turn_right)
7032 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7033 else if (should_turn_left && should_move_on)
7034 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7035 else if (should_turn_right && should_move_on)
7036 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7037 else if (should_turn_left)
7038 MovDir[x][y] = left_dir;
7039 else if (should_turn_right)
7040 MovDir[x][y] = right_dir;
7041 else if (should_move_on)
7042 MovDir[x][y] = old_move_dir;
7044 else if (can_move_on && rnd > rnd_value / 8)
7045 MovDir[x][y] = old_move_dir;
7046 else if (can_turn_left && can_turn_right)
7047 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7048 else if (can_turn_left && rnd > rnd_value / 8)
7049 MovDir[x][y] = left_dir;
7050 else if (can_turn_right && rnd > rnd_value/8)
7051 MovDir[x][y] = right_dir;
7053 MovDir[x][y] = back_dir;
7055 xx = x + move_xy[MovDir[x][y]].dx;
7056 yy = y + move_xy[MovDir[x][y]].dy;
7058 if (!IN_LEV_FIELD(xx, yy) ||
7059 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7060 MovDir[x][y] = old_move_dir;
7064 else if (element == EL_DRAGON)
7066 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7067 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7068 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7070 int rnd = RND(rnd_value);
7072 if (can_move_on && rnd > rnd_value / 8)
7073 MovDir[x][y] = old_move_dir;
7074 else if (can_turn_left && can_turn_right)
7075 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7076 else if (can_turn_left && rnd > rnd_value / 8)
7077 MovDir[x][y] = left_dir;
7078 else if (can_turn_right && rnd > rnd_value / 8)
7079 MovDir[x][y] = right_dir;
7081 MovDir[x][y] = back_dir;
7083 xx = x + move_xy[MovDir[x][y]].dx;
7084 yy = y + move_xy[MovDir[x][y]].dy;
7086 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7087 MovDir[x][y] = old_move_dir;
7091 else if (element == EL_MOLE)
7093 boolean can_move_on =
7094 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7095 IS_AMOEBOID(Feld[move_x][move_y]) ||
7096 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7099 boolean can_turn_left =
7100 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7101 IS_AMOEBOID(Feld[left_x][left_y])));
7103 boolean can_turn_right =
7104 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7105 IS_AMOEBOID(Feld[right_x][right_y])));
7107 if (can_turn_left && can_turn_right)
7108 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7109 else if (can_turn_left)
7110 MovDir[x][y] = left_dir;
7112 MovDir[x][y] = right_dir;
7115 if (MovDir[x][y] != old_move_dir)
7118 else if (element == EL_BALLOON)
7120 MovDir[x][y] = game.wind_direction;
7123 else if (element == EL_SPRING)
7125 #if USE_NEW_SPRING_BUMPER
7126 if (MovDir[x][y] & MV_HORIZONTAL)
7128 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7129 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7131 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7132 ResetGfxAnimation(move_x, move_y);
7133 TEST_DrawLevelField(move_x, move_y);
7135 MovDir[x][y] = back_dir;
7137 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7138 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7139 MovDir[x][y] = MV_NONE;
7142 if (MovDir[x][y] & MV_HORIZONTAL &&
7143 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7144 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7145 MovDir[x][y] = MV_NONE;
7150 else if (element == EL_ROBOT ||
7151 element == EL_SATELLITE ||
7152 element == EL_PENGUIN ||
7153 element == EL_EMC_ANDROID)
7155 int attr_x = -1, attr_y = -1;
7166 for (i = 0; i < MAX_PLAYERS; i++)
7168 struct PlayerInfo *player = &stored_player[i];
7169 int jx = player->jx, jy = player->jy;
7171 if (!player->active)
7175 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7183 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7184 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7185 game.engine_version < VERSION_IDENT(3,1,0,0)))
7191 if (element == EL_PENGUIN)
7194 static int xy[4][2] =
7202 for (i = 0; i < NUM_DIRECTIONS; i++)
7204 int ex = x + xy[i][0];
7205 int ey = y + xy[i][1];
7207 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7208 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7209 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7210 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7219 MovDir[x][y] = MV_NONE;
7221 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7222 else if (attr_x > x)
7223 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7225 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7226 else if (attr_y > y)
7227 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7229 if (element == EL_ROBOT)
7233 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7234 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7235 Moving2Blocked(x, y, &newx, &newy);
7237 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7238 MovDelay[x][y] = 8 + 8 * !RND(3);
7240 MovDelay[x][y] = 16;
7242 else if (element == EL_PENGUIN)
7248 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7250 boolean first_horiz = RND(2);
7251 int new_move_dir = MovDir[x][y];
7254 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7255 Moving2Blocked(x, y, &newx, &newy);
7257 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7261 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7262 Moving2Blocked(x, y, &newx, &newy);
7264 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7267 MovDir[x][y] = old_move_dir;
7271 else if (element == EL_SATELLITE)
7277 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7279 boolean first_horiz = RND(2);
7280 int new_move_dir = MovDir[x][y];
7283 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7284 Moving2Blocked(x, y, &newx, &newy);
7286 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7290 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7291 Moving2Blocked(x, y, &newx, &newy);
7293 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7296 MovDir[x][y] = old_move_dir;
7300 else if (element == EL_EMC_ANDROID)
7302 static int check_pos[16] =
7304 -1, /* 0 => (invalid) */
7305 7, /* 1 => MV_LEFT */
7306 3, /* 2 => MV_RIGHT */
7307 -1, /* 3 => (invalid) */
7309 0, /* 5 => MV_LEFT | MV_UP */
7310 2, /* 6 => MV_RIGHT | MV_UP */
7311 -1, /* 7 => (invalid) */
7312 5, /* 8 => MV_DOWN */
7313 6, /* 9 => MV_LEFT | MV_DOWN */
7314 4, /* 10 => MV_RIGHT | MV_DOWN */
7315 -1, /* 11 => (invalid) */
7316 -1, /* 12 => (invalid) */
7317 -1, /* 13 => (invalid) */
7318 -1, /* 14 => (invalid) */
7319 -1, /* 15 => (invalid) */
7327 { -1, -1, MV_LEFT | MV_UP },
7329 { +1, -1, MV_RIGHT | MV_UP },
7330 { +1, 0, MV_RIGHT },
7331 { +1, +1, MV_RIGHT | MV_DOWN },
7333 { -1, +1, MV_LEFT | MV_DOWN },
7336 int start_pos, check_order;
7337 boolean can_clone = FALSE;
7340 /* check if there is any free field around current position */
7341 for (i = 0; i < 8; i++)
7343 int newx = x + check_xy[i].dx;
7344 int newy = y + check_xy[i].dy;
7346 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7354 if (can_clone) /* randomly find an element to clone */
7358 start_pos = check_pos[RND(8)];
7359 check_order = (RND(2) ? -1 : +1);
7361 for (i = 0; i < 8; i++)
7363 int pos_raw = start_pos + i * check_order;
7364 int pos = (pos_raw + 8) % 8;
7365 int newx = x + check_xy[pos].dx;
7366 int newy = y + check_xy[pos].dy;
7368 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7370 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7371 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7373 Store[x][y] = Feld[newx][newy];
7382 if (can_clone) /* randomly find a direction to move */
7386 start_pos = check_pos[RND(8)];
7387 check_order = (RND(2) ? -1 : +1);
7389 for (i = 0; i < 8; i++)
7391 int pos_raw = start_pos + i * check_order;
7392 int pos = (pos_raw + 8) % 8;
7393 int newx = x + check_xy[pos].dx;
7394 int newy = y + check_xy[pos].dy;
7395 int new_move_dir = check_xy[pos].dir;
7397 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7399 MovDir[x][y] = new_move_dir;
7400 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7409 if (can_clone) /* cloning and moving successful */
7412 /* cannot clone -- try to move towards player */
7414 start_pos = check_pos[MovDir[x][y] & 0x0f];
7415 check_order = (RND(2) ? -1 : +1);
7417 for (i = 0; i < 3; i++)
7419 /* first check start_pos, then previous/next or (next/previous) pos */
7420 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7421 int pos = (pos_raw + 8) % 8;
7422 int newx = x + check_xy[pos].dx;
7423 int newy = y + check_xy[pos].dy;
7424 int new_move_dir = check_xy[pos].dir;
7426 if (IS_PLAYER(newx, newy))
7429 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7431 MovDir[x][y] = new_move_dir;
7432 MovDelay[x][y] = level.android_move_time * 8 + 1;
7439 else if (move_pattern == MV_TURNING_LEFT ||
7440 move_pattern == MV_TURNING_RIGHT ||
7441 move_pattern == MV_TURNING_LEFT_RIGHT ||
7442 move_pattern == MV_TURNING_RIGHT_LEFT ||
7443 move_pattern == MV_TURNING_RANDOM ||
7444 move_pattern == MV_ALL_DIRECTIONS)
7446 boolean can_turn_left =
7447 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7448 boolean can_turn_right =
7449 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7451 if (element_info[element].move_stepsize == 0) /* "not moving" */
7454 if (move_pattern == MV_TURNING_LEFT)
7455 MovDir[x][y] = left_dir;
7456 else if (move_pattern == MV_TURNING_RIGHT)
7457 MovDir[x][y] = right_dir;
7458 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7459 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7460 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7461 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7462 else if (move_pattern == MV_TURNING_RANDOM)
7463 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7464 can_turn_right && !can_turn_left ? right_dir :
7465 RND(2) ? left_dir : right_dir);
7466 else if (can_turn_left && can_turn_right)
7467 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7468 else if (can_turn_left)
7469 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7470 else if (can_turn_right)
7471 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7473 MovDir[x][y] = back_dir;
7475 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7477 else if (move_pattern == MV_HORIZONTAL ||
7478 move_pattern == MV_VERTICAL)
7480 if (move_pattern & old_move_dir)
7481 MovDir[x][y] = back_dir;
7482 else if (move_pattern == MV_HORIZONTAL)
7483 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7484 else if (move_pattern == MV_VERTICAL)
7485 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7487 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7489 else if (move_pattern & MV_ANY_DIRECTION)
7491 MovDir[x][y] = move_pattern;
7492 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7494 else if (move_pattern & MV_WIND_DIRECTION)
7496 MovDir[x][y] = game.wind_direction;
7497 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7499 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7501 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7502 MovDir[x][y] = left_dir;
7503 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7504 MovDir[x][y] = right_dir;
7506 if (MovDir[x][y] != old_move_dir)
7507 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7509 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7511 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7512 MovDir[x][y] = right_dir;
7513 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7514 MovDir[x][y] = left_dir;
7516 if (MovDir[x][y] != old_move_dir)
7517 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7519 else if (move_pattern == MV_TOWARDS_PLAYER ||
7520 move_pattern == MV_AWAY_FROM_PLAYER)
7522 int attr_x = -1, attr_y = -1;
7524 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7535 for (i = 0; i < MAX_PLAYERS; i++)
7537 struct PlayerInfo *player = &stored_player[i];
7538 int jx = player->jx, jy = player->jy;
7540 if (!player->active)
7544 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7552 MovDir[x][y] = MV_NONE;
7554 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7555 else if (attr_x > x)
7556 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7558 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7559 else if (attr_y > y)
7560 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7562 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7564 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7566 boolean first_horiz = RND(2);
7567 int new_move_dir = MovDir[x][y];
7569 if (element_info[element].move_stepsize == 0) /* "not moving" */
7571 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7572 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7578 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7579 Moving2Blocked(x, y, &newx, &newy);
7581 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7585 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7586 Moving2Blocked(x, y, &newx, &newy);
7588 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7591 MovDir[x][y] = old_move_dir;
7594 else if (move_pattern == MV_WHEN_PUSHED ||
7595 move_pattern == MV_WHEN_DROPPED)
7597 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7598 MovDir[x][y] = MV_NONE;
7602 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7604 static int test_xy[7][2] =
7614 static int test_dir[7] =
7624 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7625 int move_preference = -1000000; /* start with very low preference */
7626 int new_move_dir = MV_NONE;
7627 int start_test = RND(4);
7630 for (i = 0; i < NUM_DIRECTIONS; i++)
7632 int move_dir = test_dir[start_test + i];
7633 int move_dir_preference;
7635 xx = x + test_xy[start_test + i][0];
7636 yy = y + test_xy[start_test + i][1];
7638 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7639 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7641 new_move_dir = move_dir;
7646 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7649 move_dir_preference = -1 * RunnerVisit[xx][yy];
7650 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7651 move_dir_preference = PlayerVisit[xx][yy];
7653 if (move_dir_preference > move_preference)
7655 /* prefer field that has not been visited for the longest time */
7656 move_preference = move_dir_preference;
7657 new_move_dir = move_dir;
7659 else if (move_dir_preference == move_preference &&
7660 move_dir == old_move_dir)
7662 /* prefer last direction when all directions are preferred equally */
7663 move_preference = move_dir_preference;
7664 new_move_dir = move_dir;
7668 MovDir[x][y] = new_move_dir;
7669 if (old_move_dir != new_move_dir)
7670 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674 static void TurnRound(int x, int y)
7676 int direction = MovDir[x][y];
7680 GfxDir[x][y] = MovDir[x][y];
7682 if (direction != MovDir[x][y])
7686 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7688 ResetGfxFrame(x, y, FALSE);
7691 static boolean JustBeingPushed(int x, int y)
7695 for (i = 0; i < MAX_PLAYERS; i++)
7697 struct PlayerInfo *player = &stored_player[i];
7699 if (player->active && player->is_pushing && player->MovPos)
7701 int next_jx = player->jx + (player->jx - player->last_jx);
7702 int next_jy = player->jy + (player->jy - player->last_jy);
7704 if (x == next_jx && y == next_jy)
7712 void StartMoving(int x, int y)
7714 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7715 int element = Feld[x][y];
7720 if (MovDelay[x][y] == 0)
7721 GfxAction[x][y] = ACTION_DEFAULT;
7723 if (CAN_FALL(element) && y < lev_fieldy - 1)
7725 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7726 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7727 if (JustBeingPushed(x, y))
7730 if (element == EL_QUICKSAND_FULL)
7732 if (IS_FREE(x, y + 1))
7734 InitMovingField(x, y, MV_DOWN);
7735 started_moving = TRUE;
7737 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7738 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7739 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7740 Store[x][y] = EL_ROCK;
7742 Store[x][y] = EL_ROCK;
7745 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7747 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7749 if (!MovDelay[x][y])
7751 MovDelay[x][y] = TILEY + 1;
7753 ResetGfxAnimation(x, y);
7754 ResetGfxAnimation(x, y + 1);
7759 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7760 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7767 Feld[x][y] = EL_QUICKSAND_EMPTY;
7768 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7769 Store[x][y + 1] = Store[x][y];
7772 PlayLevelSoundAction(x, y, ACTION_FILLING);
7774 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7776 if (!MovDelay[x][y])
7778 MovDelay[x][y] = TILEY + 1;
7780 ResetGfxAnimation(x, y);
7781 ResetGfxAnimation(x, y + 1);
7786 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7787 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7794 Feld[x][y] = EL_QUICKSAND_EMPTY;
7795 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7796 Store[x][y + 1] = Store[x][y];
7799 PlayLevelSoundAction(x, y, ACTION_FILLING);
7802 else if (element == EL_QUICKSAND_FAST_FULL)
7804 if (IS_FREE(x, y + 1))
7806 InitMovingField(x, y, MV_DOWN);
7807 started_moving = TRUE;
7809 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7810 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7811 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7812 Store[x][y] = EL_ROCK;
7814 Store[x][y] = EL_ROCK;
7817 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7819 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7821 if (!MovDelay[x][y])
7823 MovDelay[x][y] = TILEY + 1;
7825 ResetGfxAnimation(x, y);
7826 ResetGfxAnimation(x, y + 1);
7831 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7832 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7839 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7840 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7841 Store[x][y + 1] = Store[x][y];
7844 PlayLevelSoundAction(x, y, ACTION_FILLING);
7846 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7848 if (!MovDelay[x][y])
7850 MovDelay[x][y] = TILEY + 1;
7852 ResetGfxAnimation(x, y);
7853 ResetGfxAnimation(x, y + 1);
7858 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7859 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7866 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7867 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7868 Store[x][y + 1] = Store[x][y];
7871 PlayLevelSoundAction(x, y, ACTION_FILLING);
7874 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7875 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7877 InitMovingField(x, y, MV_DOWN);
7878 started_moving = TRUE;
7880 Feld[x][y] = EL_QUICKSAND_FILLING;
7881 Store[x][y] = element;
7883 PlayLevelSoundAction(x, y, ACTION_FILLING);
7885 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7886 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7888 InitMovingField(x, y, MV_DOWN);
7889 started_moving = TRUE;
7891 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7892 Store[x][y] = element;
7894 PlayLevelSoundAction(x, y, ACTION_FILLING);
7896 else if (element == EL_MAGIC_WALL_FULL)
7898 if (IS_FREE(x, y + 1))
7900 InitMovingField(x, y, MV_DOWN);
7901 started_moving = TRUE;
7903 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7904 Store[x][y] = EL_CHANGED(Store[x][y]);
7906 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7908 if (!MovDelay[x][y])
7909 MovDelay[x][y] = TILEY/4 + 1;
7918 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7919 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7920 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7924 else if (element == EL_BD_MAGIC_WALL_FULL)
7926 if (IS_FREE(x, y + 1))
7928 InitMovingField(x, y, MV_DOWN);
7929 started_moving = TRUE;
7931 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7932 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7934 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7936 if (!MovDelay[x][y])
7937 MovDelay[x][y] = TILEY/4 + 1;
7946 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7947 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7948 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7952 else if (element == EL_DC_MAGIC_WALL_FULL)
7954 if (IS_FREE(x, y + 1))
7956 InitMovingField(x, y, MV_DOWN);
7957 started_moving = TRUE;
7959 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7960 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7962 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7964 if (!MovDelay[x][y])
7965 MovDelay[x][y] = TILEY/4 + 1;
7974 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7975 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7976 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7980 else if ((CAN_PASS_MAGIC_WALL(element) &&
7981 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7982 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7983 (CAN_PASS_DC_MAGIC_WALL(element) &&
7984 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7987 InitMovingField(x, y, MV_DOWN);
7988 started_moving = TRUE;
7991 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7992 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7993 EL_DC_MAGIC_WALL_FILLING);
7994 Store[x][y] = element;
7996 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7998 SplashAcid(x, y + 1);
8000 InitMovingField(x, y, MV_DOWN);
8001 started_moving = TRUE;
8003 Store[x][y] = EL_ACID;
8006 #if USE_FIX_IMPACT_COLLISION
8007 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8008 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8010 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8011 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8013 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8014 CAN_FALL(element) && WasJustFalling[x][y] &&
8015 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8017 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8018 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8019 (Feld[x][y + 1] == EL_BLOCKED)))
8021 /* this is needed for a special case not covered by calling "Impact()"
8022 from "ContinueMoving()": if an element moves to a tile directly below
8023 another element which was just falling on that tile (which was empty
8024 in the previous frame), the falling element above would just stop
8025 instead of smashing the element below (in previous version, the above
8026 element was just checked for "moving" instead of "falling", resulting
8027 in incorrect smashes caused by horizontal movement of the above
8028 element; also, the case of the player being the element to smash was
8029 simply not covered here... :-/ ) */
8031 CheckCollision[x][y] = 0;
8032 CheckImpact[x][y] = 0;
8036 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8038 if (MovDir[x][y] == MV_NONE)
8040 InitMovingField(x, y, MV_DOWN);
8041 started_moving = TRUE;
8044 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8046 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8047 MovDir[x][y] = MV_DOWN;
8049 InitMovingField(x, y, MV_DOWN);
8050 started_moving = TRUE;
8052 else if (element == EL_AMOEBA_DROP)
8054 Feld[x][y] = EL_AMOEBA_GROWING;
8055 Store[x][y] = EL_AMOEBA_WET;
8057 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8058 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8059 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8060 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8062 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8063 (IS_FREE(x - 1, y + 1) ||
8064 Feld[x - 1][y + 1] == EL_ACID));
8065 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8066 (IS_FREE(x + 1, y + 1) ||
8067 Feld[x + 1][y + 1] == EL_ACID));
8068 boolean can_fall_any = (can_fall_left || can_fall_right);
8069 boolean can_fall_both = (can_fall_left && can_fall_right);
8070 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8072 #if USE_NEW_ALL_SLIPPERY
8073 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8075 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8076 can_fall_right = FALSE;
8077 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8078 can_fall_left = FALSE;
8079 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8080 can_fall_right = FALSE;
8081 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8082 can_fall_left = FALSE;
8084 can_fall_any = (can_fall_left || can_fall_right);
8085 can_fall_both = FALSE;
8088 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8090 if (slippery_type == SLIPPERY_ONLY_LEFT)
8091 can_fall_right = FALSE;
8092 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8093 can_fall_left = FALSE;
8094 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8095 can_fall_right = FALSE;
8096 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8097 can_fall_left = FALSE;
8099 can_fall_any = (can_fall_left || can_fall_right);
8100 can_fall_both = (can_fall_left && can_fall_right);
8104 #if USE_NEW_ALL_SLIPPERY
8106 #if USE_NEW_SP_SLIPPERY
8107 /* !!! better use the same properties as for custom elements here !!! */
8108 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8109 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8111 can_fall_right = FALSE; /* slip down on left side */
8112 can_fall_both = FALSE;
8117 #if USE_NEW_ALL_SLIPPERY
8120 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8121 can_fall_right = FALSE; /* slip down on left side */
8123 can_fall_left = !(can_fall_right = RND(2));
8125 can_fall_both = FALSE;
8130 if (game.emulation == EMU_BOULDERDASH ||
8131 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8132 can_fall_right = FALSE; /* slip down on left side */
8134 can_fall_left = !(can_fall_right = RND(2));
8136 can_fall_both = FALSE;
8142 /* if not determined otherwise, prefer left side for slipping down */
8143 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8144 started_moving = TRUE;
8148 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8150 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8153 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8154 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8155 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8156 int belt_dir = game.belt_dir[belt_nr];
8158 if ((belt_dir == MV_LEFT && left_is_free) ||
8159 (belt_dir == MV_RIGHT && right_is_free))
8161 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8163 InitMovingField(x, y, belt_dir);
8164 started_moving = TRUE;
8166 Pushed[x][y] = TRUE;
8167 Pushed[nextx][y] = TRUE;
8169 GfxAction[x][y] = ACTION_DEFAULT;
8173 MovDir[x][y] = 0; /* if element was moving, stop it */
8178 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8180 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8182 if (CAN_MOVE(element) && !started_moving)
8185 int move_pattern = element_info[element].move_pattern;
8190 if (MovDir[x][y] == MV_NONE)
8192 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8193 x, y, element, element_info[element].token_name);
8194 printf("StartMoving(): This should never happen!\n");
8199 Moving2Blocked(x, y, &newx, &newy);
8201 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8204 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8205 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8207 WasJustMoving[x][y] = 0;
8208 CheckCollision[x][y] = 0;
8210 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8212 if (Feld[x][y] != element) /* element has changed */
8216 if (!MovDelay[x][y]) /* start new movement phase */
8218 /* all objects that can change their move direction after each step
8219 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8221 if (element != EL_YAMYAM &&
8222 element != EL_DARK_YAMYAM &&
8223 element != EL_PACMAN &&
8224 !(move_pattern & MV_ANY_DIRECTION) &&
8225 move_pattern != MV_TURNING_LEFT &&
8226 move_pattern != MV_TURNING_RIGHT &&
8227 move_pattern != MV_TURNING_LEFT_RIGHT &&
8228 move_pattern != MV_TURNING_RIGHT_LEFT &&
8229 move_pattern != MV_TURNING_RANDOM)
8233 if (MovDelay[x][y] && (element == EL_BUG ||
8234 element == EL_SPACESHIP ||
8235 element == EL_SP_SNIKSNAK ||
8236 element == EL_SP_ELECTRON ||
8237 element == EL_MOLE))
8238 TEST_DrawLevelField(x, y);
8242 if (MovDelay[x][y]) /* wait some time before next movement */
8246 if (element == EL_ROBOT ||
8247 element == EL_YAMYAM ||
8248 element == EL_DARK_YAMYAM)
8250 DrawLevelElementAnimationIfNeeded(x, y, element);
8251 PlayLevelSoundAction(x, y, ACTION_WAITING);
8253 else if (element == EL_SP_ELECTRON)
8254 DrawLevelElementAnimationIfNeeded(x, y, element);
8255 else if (element == EL_DRAGON)
8258 int dir = MovDir[x][y];
8259 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8260 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8261 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8262 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8263 dir == MV_UP ? IMG_FLAMES_1_UP :
8264 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8265 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8267 GfxAction[x][y] = ACTION_ATTACKING;
8269 if (IS_PLAYER(x, y))
8270 DrawPlayerField(x, y);
8272 TEST_DrawLevelField(x, y);
8274 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8276 for (i = 1; i <= 3; i++)
8278 int xx = x + i * dx;
8279 int yy = y + i * dy;
8280 int sx = SCREENX(xx);
8281 int sy = SCREENY(yy);
8282 int flame_graphic = graphic + (i - 1);
8284 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8289 int flamed = MovingOrBlocked2Element(xx, yy);
8293 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8295 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8296 RemoveMovingField(xx, yy);
8298 RemoveField(xx, yy);
8300 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8303 RemoveMovingField(xx, yy);
8306 ChangeDelay[xx][yy] = 0;
8308 Feld[xx][yy] = EL_FLAMES;
8310 if (IN_SCR_FIELD(sx, sy))
8312 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8313 DrawGraphic(sx, sy, flame_graphic, frame);
8318 if (Feld[xx][yy] == EL_FLAMES)
8319 Feld[xx][yy] = EL_EMPTY;
8320 TEST_DrawLevelField(xx, yy);
8325 if (MovDelay[x][y]) /* element still has to wait some time */
8327 PlayLevelSoundAction(x, y, ACTION_WAITING);
8333 /* now make next step */
8335 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8337 if (DONT_COLLIDE_WITH(element) &&
8338 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8339 !PLAYER_ENEMY_PROTECTED(newx, newy))
8341 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8346 else if (CAN_MOVE_INTO_ACID(element) &&
8347 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8348 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8349 (MovDir[x][y] == MV_DOWN ||
8350 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8352 SplashAcid(newx, newy);
8353 Store[x][y] = EL_ACID;
8355 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8357 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8358 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8359 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8360 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8363 TEST_DrawLevelField(x, y);
8365 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8366 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8367 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8369 local_player->friends_still_needed--;
8370 if (!local_player->friends_still_needed &&
8371 !local_player->GameOver && AllPlayersGone)
8372 PlayerWins(local_player);
8376 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8378 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8379 TEST_DrawLevelField(newx, newy);
8381 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8383 else if (!IS_FREE(newx, newy))
8385 GfxAction[x][y] = ACTION_WAITING;
8387 if (IS_PLAYER(x, y))
8388 DrawPlayerField(x, y);
8390 TEST_DrawLevelField(x, y);
8395 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8397 if (IS_FOOD_PIG(Feld[newx][newy]))
8399 if (IS_MOVING(newx, newy))
8400 RemoveMovingField(newx, newy);
8403 Feld[newx][newy] = EL_EMPTY;
8404 TEST_DrawLevelField(newx, newy);
8407 PlayLevelSound(x, y, SND_PIG_DIGGING);
8409 else if (!IS_FREE(newx, newy))
8411 if (IS_PLAYER(x, y))
8412 DrawPlayerField(x, y);
8414 TEST_DrawLevelField(x, y);
8419 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8421 if (Store[x][y] != EL_EMPTY)
8423 boolean can_clone = FALSE;
8426 /* check if element to clone is still there */
8427 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8429 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8437 /* cannot clone or target field not free anymore -- do not clone */
8438 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8439 Store[x][y] = EL_EMPTY;
8442 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8444 if (IS_MV_DIAGONAL(MovDir[x][y]))
8446 int diagonal_move_dir = MovDir[x][y];
8447 int stored = Store[x][y];
8448 int change_delay = 8;
8451 /* android is moving diagonally */
8453 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8455 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8456 GfxElement[x][y] = EL_EMC_ANDROID;
8457 GfxAction[x][y] = ACTION_SHRINKING;
8458 GfxDir[x][y] = diagonal_move_dir;
8459 ChangeDelay[x][y] = change_delay;
8461 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8464 DrawLevelGraphicAnimation(x, y, graphic);
8465 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8467 if (Feld[newx][newy] == EL_ACID)
8469 SplashAcid(newx, newy);
8474 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8476 Store[newx][newy] = EL_EMC_ANDROID;
8477 GfxElement[newx][newy] = EL_EMC_ANDROID;
8478 GfxAction[newx][newy] = ACTION_GROWING;
8479 GfxDir[newx][newy] = diagonal_move_dir;
8480 ChangeDelay[newx][newy] = change_delay;
8482 graphic = el_act_dir2img(GfxElement[newx][newy],
8483 GfxAction[newx][newy], GfxDir[newx][newy]);
8485 DrawLevelGraphicAnimation(newx, newy, graphic);
8486 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8492 Feld[newx][newy] = EL_EMPTY;
8493 TEST_DrawLevelField(newx, newy);
8495 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8498 else if (!IS_FREE(newx, newy))
8501 if (IS_PLAYER(x, y))
8502 DrawPlayerField(x, y);
8504 TEST_DrawLevelField(x, y);
8510 else if (IS_CUSTOM_ELEMENT(element) &&
8511 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8514 if (!DigFieldByCE(newx, newy, element))
8517 int new_element = Feld[newx][newy];
8519 if (!IS_FREE(newx, newy))
8521 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8522 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8525 /* no element can dig solid indestructible elements */
8526 if (IS_INDESTRUCTIBLE(new_element) &&
8527 !IS_DIGGABLE(new_element) &&
8528 !IS_COLLECTIBLE(new_element))
8531 if (AmoebaNr[newx][newy] &&
8532 (new_element == EL_AMOEBA_FULL ||
8533 new_element == EL_BD_AMOEBA ||
8534 new_element == EL_AMOEBA_GROWING))
8536 AmoebaCnt[AmoebaNr[newx][newy]]--;
8537 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8540 if (IS_MOVING(newx, newy))
8541 RemoveMovingField(newx, newy);
8544 RemoveField(newx, newy);
8545 TEST_DrawLevelField(newx, newy);
8548 /* if digged element was about to explode, prevent the explosion */
8549 ExplodeField[newx][newy] = EX_TYPE_NONE;
8551 PlayLevelSoundAction(x, y, action);
8554 Store[newx][newy] = EL_EMPTY;
8557 /* this makes it possible to leave the removed element again */
8558 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8559 Store[newx][newy] = new_element;
8561 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8563 int move_leave_element = element_info[element].move_leave_element;
8565 /* this makes it possible to leave the removed element again */
8566 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8567 new_element : move_leave_element);
8573 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8575 RunnerVisit[x][y] = FrameCounter;
8576 PlayerVisit[x][y] /= 8; /* expire player visit path */
8579 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8581 if (!IS_FREE(newx, newy))
8583 if (IS_PLAYER(x, y))
8584 DrawPlayerField(x, y);
8586 TEST_DrawLevelField(x, y);
8592 boolean wanna_flame = !RND(10);
8593 int dx = newx - x, dy = newy - y;
8594 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8595 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8596 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8597 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8598 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8599 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8602 IS_CLASSIC_ENEMY(element1) ||
8603 IS_CLASSIC_ENEMY(element2)) &&
8604 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8605 element1 != EL_FLAMES && element2 != EL_FLAMES)
8607 ResetGfxAnimation(x, y);
8608 GfxAction[x][y] = ACTION_ATTACKING;
8610 if (IS_PLAYER(x, y))
8611 DrawPlayerField(x, y);
8613 TEST_DrawLevelField(x, y);
8615 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8617 MovDelay[x][y] = 50;
8621 RemoveField(newx, newy);
8623 Feld[newx][newy] = EL_FLAMES;
8624 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8627 RemoveField(newx1, newy1);
8629 Feld[newx1][newy1] = EL_FLAMES;
8631 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8634 RemoveField(newx2, newy2);
8636 Feld[newx2][newy2] = EL_FLAMES;
8643 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8644 Feld[newx][newy] == EL_DIAMOND)
8646 if (IS_MOVING(newx, newy))
8647 RemoveMovingField(newx, newy);
8650 Feld[newx][newy] = EL_EMPTY;
8651 TEST_DrawLevelField(newx, newy);
8654 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8656 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8657 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8659 if (AmoebaNr[newx][newy])
8661 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8662 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8663 Feld[newx][newy] == EL_BD_AMOEBA)
8664 AmoebaCnt[AmoebaNr[newx][newy]]--;
8669 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8671 RemoveMovingField(newx, newy);
8674 if (IS_MOVING(newx, newy))
8676 RemoveMovingField(newx, newy);
8681 Feld[newx][newy] = EL_EMPTY;
8682 TEST_DrawLevelField(newx, newy);
8685 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8687 else if ((element == EL_PACMAN || element == EL_MOLE)
8688 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8690 if (AmoebaNr[newx][newy])
8692 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8693 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8694 Feld[newx][newy] == EL_BD_AMOEBA)
8695 AmoebaCnt[AmoebaNr[newx][newy]]--;
8698 if (element == EL_MOLE)
8700 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8701 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8703 ResetGfxAnimation(x, y);
8704 GfxAction[x][y] = ACTION_DIGGING;
8705 TEST_DrawLevelField(x, y);
8707 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8709 return; /* wait for shrinking amoeba */
8711 else /* element == EL_PACMAN */
8713 Feld[newx][newy] = EL_EMPTY;
8714 TEST_DrawLevelField(newx, newy);
8715 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8718 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8719 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8720 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8722 /* wait for shrinking amoeba to completely disappear */
8725 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8727 /* object was running against a wall */
8732 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8733 if (move_pattern & MV_ANY_DIRECTION &&
8734 move_pattern == MovDir[x][y])
8736 int blocking_element =
8737 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8739 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8742 element = Feld[x][y]; /* element might have changed */
8746 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8747 DrawLevelElementAnimation(x, y, element);
8749 if (DONT_TOUCH(element))
8750 TestIfBadThingTouchesPlayer(x, y);
8755 InitMovingField(x, y, MovDir[x][y]);
8757 PlayLevelSoundAction(x, y, ACTION_MOVING);
8761 ContinueMoving(x, y);
8764 void ContinueMoving(int x, int y)
8766 int element = Feld[x][y];
8767 struct ElementInfo *ei = &element_info[element];
8768 int direction = MovDir[x][y];
8769 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8770 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8771 int newx = x + dx, newy = y + dy;
8772 int stored = Store[x][y];
8773 int stored_new = Store[newx][newy];
8774 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8775 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8776 boolean last_line = (newy == lev_fieldy - 1);
8778 MovPos[x][y] += getElementMoveStepsize(x, y);
8780 if (pushed_by_player) /* special case: moving object pushed by player */
8781 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8783 if (ABS(MovPos[x][y]) < TILEX)
8786 int ee = Feld[x][y];
8787 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8788 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8790 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8791 x, y, ABS(MovPos[x][y]),
8793 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8796 TEST_DrawLevelField(x, y);
8798 return; /* element is still moving */
8801 /* element reached destination field */
8803 Feld[x][y] = EL_EMPTY;
8804 Feld[newx][newy] = element;
8805 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8807 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8809 element = Feld[newx][newy] = EL_ACID;
8811 else if (element == EL_MOLE)
8813 Feld[x][y] = EL_SAND;
8815 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8817 else if (element == EL_QUICKSAND_FILLING)
8819 element = Feld[newx][newy] = get_next_element(element);
8820 Store[newx][newy] = Store[x][y];
8822 else if (element == EL_QUICKSAND_EMPTYING)
8824 Feld[x][y] = get_next_element(element);
8825 element = Feld[newx][newy] = Store[x][y];
8827 else if (element == EL_QUICKSAND_FAST_FILLING)
8829 element = Feld[newx][newy] = get_next_element(element);
8830 Store[newx][newy] = Store[x][y];
8832 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8834 Feld[x][y] = get_next_element(element);
8835 element = Feld[newx][newy] = Store[x][y];
8837 else if (element == EL_MAGIC_WALL_FILLING)
8839 element = Feld[newx][newy] = get_next_element(element);
8840 if (!game.magic_wall_active)
8841 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8842 Store[newx][newy] = Store[x][y];
8844 else if (element == EL_MAGIC_WALL_EMPTYING)
8846 Feld[x][y] = get_next_element(element);
8847 if (!game.magic_wall_active)
8848 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8849 element = Feld[newx][newy] = Store[x][y];
8851 #if USE_NEW_CUSTOM_VALUE
8852 InitField(newx, newy, FALSE);
8855 else if (element == EL_BD_MAGIC_WALL_FILLING)
8857 element = Feld[newx][newy] = get_next_element(element);
8858 if (!game.magic_wall_active)
8859 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8860 Store[newx][newy] = Store[x][y];
8862 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8864 Feld[x][y] = get_next_element(element);
8865 if (!game.magic_wall_active)
8866 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8867 element = Feld[newx][newy] = Store[x][y];
8869 #if USE_NEW_CUSTOM_VALUE
8870 InitField(newx, newy, FALSE);
8873 else if (element == EL_DC_MAGIC_WALL_FILLING)
8875 element = Feld[newx][newy] = get_next_element(element);
8876 if (!game.magic_wall_active)
8877 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8878 Store[newx][newy] = Store[x][y];
8880 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8882 Feld[x][y] = get_next_element(element);
8883 if (!game.magic_wall_active)
8884 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8885 element = Feld[newx][newy] = Store[x][y];
8887 #if USE_NEW_CUSTOM_VALUE
8888 InitField(newx, newy, FALSE);
8891 else if (element == EL_AMOEBA_DROPPING)
8893 Feld[x][y] = get_next_element(element);
8894 element = Feld[newx][newy] = Store[x][y];
8896 else if (element == EL_SOKOBAN_OBJECT)
8899 Feld[x][y] = Back[x][y];
8901 if (Back[newx][newy])
8902 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8904 Back[x][y] = Back[newx][newy] = 0;
8907 Store[x][y] = EL_EMPTY;
8912 MovDelay[newx][newy] = 0;
8914 if (CAN_CHANGE_OR_HAS_ACTION(element))
8916 /* copy element change control values to new field */
8917 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8918 ChangePage[newx][newy] = ChangePage[x][y];
8919 ChangeCount[newx][newy] = ChangeCount[x][y];
8920 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8923 #if USE_NEW_CUSTOM_VALUE
8924 CustomValue[newx][newy] = CustomValue[x][y];
8927 ChangeDelay[x][y] = 0;
8928 ChangePage[x][y] = -1;
8929 ChangeCount[x][y] = 0;
8930 ChangeEvent[x][y] = -1;
8932 #if USE_NEW_CUSTOM_VALUE
8933 CustomValue[x][y] = 0;
8936 /* copy animation control values to new field */
8937 GfxFrame[newx][newy] = GfxFrame[x][y];
8938 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8939 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8940 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8942 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8944 /* some elements can leave other elements behind after moving */
8946 if (ei->move_leave_element != EL_EMPTY &&
8947 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8948 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8950 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8951 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8952 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8955 int move_leave_element = ei->move_leave_element;
8959 /* this makes it possible to leave the removed element again */
8960 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8961 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8963 /* this makes it possible to leave the removed element again */
8964 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8965 move_leave_element = stored;
8968 /* this makes it possible to leave the removed element again */
8969 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8970 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8971 move_leave_element = stored;
8974 Feld[x][y] = move_leave_element;
8976 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8977 MovDir[x][y] = direction;
8979 InitField(x, y, FALSE);
8981 if (GFX_CRUMBLED(Feld[x][y]))
8982 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8984 if (ELEM_IS_PLAYER(move_leave_element))
8985 RelocatePlayer(x, y, move_leave_element);
8988 /* do this after checking for left-behind element */
8989 ResetGfxAnimation(x, y); /* reset animation values for old field */
8991 if (!CAN_MOVE(element) ||
8992 (CAN_FALL(element) && direction == MV_DOWN &&
8993 (element == EL_SPRING ||
8994 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8995 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8996 GfxDir[x][y] = MovDir[newx][newy] = 0;
8998 TEST_DrawLevelField(x, y);
8999 TEST_DrawLevelField(newx, newy);
9001 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9003 /* prevent pushed element from moving on in pushed direction */
9004 if (pushed_by_player && CAN_MOVE(element) &&
9005 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9006 !(element_info[element].move_pattern & direction))
9007 TurnRound(newx, newy);
9009 /* prevent elements on conveyor belt from moving on in last direction */
9010 if (pushed_by_conveyor && CAN_FALL(element) &&
9011 direction & MV_HORIZONTAL)
9012 MovDir[newx][newy] = 0;
9014 if (!pushed_by_player)
9016 int nextx = newx + dx, nexty = newy + dy;
9017 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9019 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9021 if (CAN_FALL(element) && direction == MV_DOWN)
9022 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9024 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9025 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9027 #if USE_FIX_IMPACT_COLLISION
9028 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9029 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9033 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9035 TestIfBadThingTouchesPlayer(newx, newy);
9036 TestIfBadThingTouchesFriend(newx, newy);
9038 if (!IS_CUSTOM_ELEMENT(element))
9039 TestIfBadThingTouchesOtherBadThing(newx, newy);
9041 else if (element == EL_PENGUIN)
9042 TestIfFriendTouchesBadThing(newx, newy);
9044 if (DONT_GET_HIT_BY(element))
9046 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9049 /* give the player one last chance (one more frame) to move away */
9050 if (CAN_FALL(element) && direction == MV_DOWN &&
9051 (last_line || (!IS_FREE(x, newy + 1) &&
9052 (!IS_PLAYER(x, newy + 1) ||
9053 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9056 if (pushed_by_player && !game.use_change_when_pushing_bug)
9058 int push_side = MV_DIR_OPPOSITE(direction);
9059 struct PlayerInfo *player = PLAYERINFO(x, y);
9061 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9062 player->index_bit, push_side);
9063 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9064 player->index_bit, push_side);
9067 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9068 MovDelay[newx][newy] = 1;
9070 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9072 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9075 if (ChangePage[newx][newy] != -1) /* delayed change */
9077 int page = ChangePage[newx][newy];
9078 struct ElementChangeInfo *change = &ei->change_page[page];
9080 ChangePage[newx][newy] = -1;
9082 if (change->can_change)
9084 if (ChangeElement(newx, newy, element, page))
9086 if (change->post_change_function)
9087 change->post_change_function(newx, newy);
9091 if (change->has_action)
9092 ExecuteCustomElementAction(newx, newy, element, page);
9096 TestIfElementHitsCustomElement(newx, newy, direction);
9097 TestIfPlayerTouchesCustomElement(newx, newy);
9098 TestIfElementTouchesCustomElement(newx, newy);
9100 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9101 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9102 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9103 MV_DIR_OPPOSITE(direction));
9106 int AmoebeNachbarNr(int ax, int ay)
9109 int element = Feld[ax][ay];
9111 static int xy[4][2] =
9119 for (i = 0; i < NUM_DIRECTIONS; i++)
9121 int x = ax + xy[i][0];
9122 int y = ay + xy[i][1];
9124 if (!IN_LEV_FIELD(x, y))
9127 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9128 group_nr = AmoebaNr[x][y];
9134 void AmoebenVereinigen(int ax, int ay)
9136 int i, x, y, xx, yy;
9137 int new_group_nr = AmoebaNr[ax][ay];
9138 static int xy[4][2] =
9146 if (new_group_nr == 0)
9149 for (i = 0; i < NUM_DIRECTIONS; i++)
9154 if (!IN_LEV_FIELD(x, y))
9157 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9158 Feld[x][y] == EL_BD_AMOEBA ||
9159 Feld[x][y] == EL_AMOEBA_DEAD) &&
9160 AmoebaNr[x][y] != new_group_nr)
9162 int old_group_nr = AmoebaNr[x][y];
9164 if (old_group_nr == 0)
9167 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9168 AmoebaCnt[old_group_nr] = 0;
9169 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9170 AmoebaCnt2[old_group_nr] = 0;
9172 SCAN_PLAYFIELD(xx, yy)
9174 if (AmoebaNr[xx][yy] == old_group_nr)
9175 AmoebaNr[xx][yy] = new_group_nr;
9181 void AmoebeUmwandeln(int ax, int ay)
9185 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9187 int group_nr = AmoebaNr[ax][ay];
9192 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9193 printf("AmoebeUmwandeln(): This should never happen!\n");
9198 SCAN_PLAYFIELD(x, y)
9200 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9203 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9207 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9208 SND_AMOEBA_TURNING_TO_GEM :
9209 SND_AMOEBA_TURNING_TO_ROCK));
9214 static int xy[4][2] =
9222 for (i = 0; i < NUM_DIRECTIONS; i++)
9227 if (!IN_LEV_FIELD(x, y))
9230 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9232 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9233 SND_AMOEBA_TURNING_TO_GEM :
9234 SND_AMOEBA_TURNING_TO_ROCK));
9241 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9244 int group_nr = AmoebaNr[ax][ay];
9245 boolean done = FALSE;
9250 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9251 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9256 SCAN_PLAYFIELD(x, y)
9258 if (AmoebaNr[x][y] == group_nr &&
9259 (Feld[x][y] == EL_AMOEBA_DEAD ||
9260 Feld[x][y] == EL_BD_AMOEBA ||
9261 Feld[x][y] == EL_AMOEBA_GROWING))
9264 Feld[x][y] = new_element;
9265 InitField(x, y, FALSE);
9266 TEST_DrawLevelField(x, y);
9272 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9273 SND_BD_AMOEBA_TURNING_TO_ROCK :
9274 SND_BD_AMOEBA_TURNING_TO_GEM));
9277 void AmoebeWaechst(int x, int y)
9279 static unsigned long sound_delay = 0;
9280 static unsigned long sound_delay_value = 0;
9282 if (!MovDelay[x][y]) /* start new growing cycle */
9286 if (DelayReached(&sound_delay, sound_delay_value))
9288 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9289 sound_delay_value = 30;
9293 if (MovDelay[x][y]) /* wait some time before growing bigger */
9296 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9298 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9299 6 - MovDelay[x][y]);
9301 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9304 if (!MovDelay[x][y])
9306 Feld[x][y] = Store[x][y];
9308 TEST_DrawLevelField(x, y);
9313 void AmoebaDisappearing(int x, int y)
9315 static unsigned long sound_delay = 0;
9316 static unsigned long sound_delay_value = 0;
9318 if (!MovDelay[x][y]) /* start new shrinking cycle */
9322 if (DelayReached(&sound_delay, sound_delay_value))
9323 sound_delay_value = 30;
9326 if (MovDelay[x][y]) /* wait some time before shrinking */
9329 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9331 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9332 6 - MovDelay[x][y]);
9334 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9337 if (!MovDelay[x][y])
9339 Feld[x][y] = EL_EMPTY;
9340 TEST_DrawLevelField(x, y);
9342 /* don't let mole enter this field in this cycle;
9343 (give priority to objects falling to this field from above) */
9349 void AmoebeAbleger(int ax, int ay)
9352 int element = Feld[ax][ay];
9353 int graphic = el2img(element);
9354 int newax = ax, neway = ay;
9355 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9356 static int xy[4][2] =
9364 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9366 Feld[ax][ay] = EL_AMOEBA_DEAD;
9367 TEST_DrawLevelField(ax, ay);
9371 if (IS_ANIMATED(graphic))
9372 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9374 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9375 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9377 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9380 if (MovDelay[ax][ay])
9384 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9387 int x = ax + xy[start][0];
9388 int y = ay + xy[start][1];
9390 if (!IN_LEV_FIELD(x, y))
9393 if (IS_FREE(x, y) ||
9394 CAN_GROW_INTO(Feld[x][y]) ||
9395 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9396 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9402 if (newax == ax && neway == ay)
9405 else /* normal or "filled" (BD style) amoeba */
9408 boolean waiting_for_player = FALSE;
9410 for (i = 0; i < NUM_DIRECTIONS; i++)
9412 int j = (start + i) % 4;
9413 int x = ax + xy[j][0];
9414 int y = ay + xy[j][1];
9416 if (!IN_LEV_FIELD(x, y))
9419 if (IS_FREE(x, y) ||
9420 CAN_GROW_INTO(Feld[x][y]) ||
9421 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9422 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9428 else if (IS_PLAYER(x, y))
9429 waiting_for_player = TRUE;
9432 if (newax == ax && neway == ay) /* amoeba cannot grow */
9434 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9436 Feld[ax][ay] = EL_AMOEBA_DEAD;
9437 TEST_DrawLevelField(ax, ay);
9438 AmoebaCnt[AmoebaNr[ax][ay]]--;
9440 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9442 if (element == EL_AMOEBA_FULL)
9443 AmoebeUmwandeln(ax, ay);
9444 else if (element == EL_BD_AMOEBA)
9445 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9450 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9452 /* amoeba gets larger by growing in some direction */
9454 int new_group_nr = AmoebaNr[ax][ay];
9457 if (new_group_nr == 0)
9459 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9460 printf("AmoebeAbleger(): This should never happen!\n");
9465 AmoebaNr[newax][neway] = new_group_nr;
9466 AmoebaCnt[new_group_nr]++;
9467 AmoebaCnt2[new_group_nr]++;
9469 /* if amoeba touches other amoeba(s) after growing, unify them */
9470 AmoebenVereinigen(newax, neway);
9472 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9474 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9480 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9481 (neway == lev_fieldy - 1 && newax != ax))
9483 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9484 Store[newax][neway] = element;
9486 else if (neway == ay || element == EL_EMC_DRIPPER)
9488 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9490 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9494 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9495 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9496 Store[ax][ay] = EL_AMOEBA_DROP;
9497 ContinueMoving(ax, ay);
9501 TEST_DrawLevelField(newax, neway);
9504 void Life(int ax, int ay)
9508 int element = Feld[ax][ay];
9509 int graphic = el2img(element);
9510 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9512 boolean changed = FALSE;
9514 if (IS_ANIMATED(graphic))
9515 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9520 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9521 MovDelay[ax][ay] = life_time;
9523 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9526 if (MovDelay[ax][ay])
9530 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9532 int xx = ax+x1, yy = ay+y1;
9535 if (!IN_LEV_FIELD(xx, yy))
9538 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9540 int x = xx+x2, y = yy+y2;
9542 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9545 if (((Feld[x][y] == element ||
9546 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9548 (IS_FREE(x, y) && Stop[x][y]))
9552 if (xx == ax && yy == ay) /* field in the middle */
9554 if (nachbarn < life_parameter[0] ||
9555 nachbarn > life_parameter[1])
9557 Feld[xx][yy] = EL_EMPTY;
9559 TEST_DrawLevelField(xx, yy);
9560 Stop[xx][yy] = TRUE;
9564 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9565 { /* free border field */
9566 if (nachbarn >= life_parameter[2] &&
9567 nachbarn <= life_parameter[3])
9569 Feld[xx][yy] = element;
9570 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9572 TEST_DrawLevelField(xx, yy);
9573 Stop[xx][yy] = TRUE;
9580 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9581 SND_GAME_OF_LIFE_GROWING);
9584 static void InitRobotWheel(int x, int y)
9586 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9589 static void RunRobotWheel(int x, int y)
9591 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9594 static void StopRobotWheel(int x, int y)
9596 if (ZX == x && ZY == y)
9600 game.robot_wheel_active = FALSE;
9604 static void InitTimegateWheel(int x, int y)
9606 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9609 static void RunTimegateWheel(int x, int y)
9611 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9614 static void InitMagicBallDelay(int x, int y)
9617 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9619 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9623 static void ActivateMagicBall(int bx, int by)
9627 if (level.ball_random)
9629 int pos_border = RND(8); /* select one of the eight border elements */
9630 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9631 int xx = pos_content % 3;
9632 int yy = pos_content / 3;
9637 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9638 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9642 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9644 int xx = x - bx + 1;
9645 int yy = y - by + 1;
9647 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9648 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9652 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9655 void CheckExit(int x, int y)
9657 if (local_player->gems_still_needed > 0 ||
9658 local_player->sokobanfields_still_needed > 0 ||
9659 local_player->lights_still_needed > 0)
9661 int element = Feld[x][y];
9662 int graphic = el2img(element);
9664 if (IS_ANIMATED(graphic))
9665 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9670 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9673 Feld[x][y] = EL_EXIT_OPENING;
9675 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9678 void CheckExitEM(int x, int y)
9680 if (local_player->gems_still_needed > 0 ||
9681 local_player->sokobanfields_still_needed > 0 ||
9682 local_player->lights_still_needed > 0)
9684 int element = Feld[x][y];
9685 int graphic = el2img(element);
9687 if (IS_ANIMATED(graphic))
9688 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9693 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9696 Feld[x][y] = EL_EM_EXIT_OPENING;
9698 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9701 void CheckExitSteel(int x, int y)
9703 if (local_player->gems_still_needed > 0 ||
9704 local_player->sokobanfields_still_needed > 0 ||
9705 local_player->lights_still_needed > 0)
9707 int element = Feld[x][y];
9708 int graphic = el2img(element);
9710 if (IS_ANIMATED(graphic))
9711 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9716 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9719 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9721 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9724 void CheckExitSteelEM(int x, int y)
9726 if (local_player->gems_still_needed > 0 ||
9727 local_player->sokobanfields_still_needed > 0 ||
9728 local_player->lights_still_needed > 0)
9730 int element = Feld[x][y];
9731 int graphic = el2img(element);
9733 if (IS_ANIMATED(graphic))
9734 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9739 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9742 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9744 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9747 void CheckExitSP(int x, int y)
9749 if (local_player->gems_still_needed > 0)
9751 int element = Feld[x][y];
9752 int graphic = el2img(element);
9754 if (IS_ANIMATED(graphic))
9755 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9760 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9763 Feld[x][y] = EL_SP_EXIT_OPENING;
9765 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9768 static void CloseAllOpenTimegates()
9772 SCAN_PLAYFIELD(x, y)
9774 int element = Feld[x][y];
9776 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9778 Feld[x][y] = EL_TIMEGATE_CLOSING;
9780 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9785 void DrawTwinkleOnField(int x, int y)
9787 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9790 if (Feld[x][y] == EL_BD_DIAMOND)
9793 if (MovDelay[x][y] == 0) /* next animation frame */
9794 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9796 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9800 DrawLevelElementAnimation(x, y, Feld[x][y]);
9802 if (MovDelay[x][y] != 0)
9804 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9805 10 - MovDelay[x][y]);
9807 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9812 void MauerWaechst(int x, int y)
9816 if (!MovDelay[x][y]) /* next animation frame */
9817 MovDelay[x][y] = 3 * delay;
9819 if (MovDelay[x][y]) /* wait some time before next frame */
9823 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9825 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9826 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9828 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9831 if (!MovDelay[x][y])
9833 if (MovDir[x][y] == MV_LEFT)
9835 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9836 TEST_DrawLevelField(x - 1, y);
9838 else if (MovDir[x][y] == MV_RIGHT)
9840 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9841 TEST_DrawLevelField(x + 1, y);
9843 else if (MovDir[x][y] == MV_UP)
9845 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9846 TEST_DrawLevelField(x, y - 1);
9850 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9851 TEST_DrawLevelField(x, y + 1);
9854 Feld[x][y] = Store[x][y];
9856 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9857 TEST_DrawLevelField(x, y);
9862 void MauerAbleger(int ax, int ay)
9864 int element = Feld[ax][ay];
9865 int graphic = el2img(element);
9866 boolean oben_frei = FALSE, unten_frei = FALSE;
9867 boolean links_frei = FALSE, rechts_frei = FALSE;
9868 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9869 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9870 boolean new_wall = FALSE;
9872 if (IS_ANIMATED(graphic))
9873 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9875 if (!MovDelay[ax][ay]) /* start building new wall */
9876 MovDelay[ax][ay] = 6;
9878 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9881 if (MovDelay[ax][ay])
9885 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9887 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9889 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9891 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9894 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9895 element == EL_EXPANDABLE_WALL_ANY)
9899 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9900 Store[ax][ay-1] = element;
9901 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9902 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9903 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9904 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9909 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9910 Store[ax][ay+1] = element;
9911 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9912 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9913 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9914 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9919 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9920 element == EL_EXPANDABLE_WALL_ANY ||
9921 element == EL_EXPANDABLE_WALL ||
9922 element == EL_BD_EXPANDABLE_WALL)
9926 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9927 Store[ax-1][ay] = element;
9928 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9929 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9930 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9931 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9937 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9938 Store[ax+1][ay] = element;
9939 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9940 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9941 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9942 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9947 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9948 TEST_DrawLevelField(ax, ay);
9950 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9952 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9953 unten_massiv = TRUE;
9954 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9955 links_massiv = TRUE;
9956 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9957 rechts_massiv = TRUE;
9959 if (((oben_massiv && unten_massiv) ||
9960 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9961 element == EL_EXPANDABLE_WALL) &&
9962 ((links_massiv && rechts_massiv) ||
9963 element == EL_EXPANDABLE_WALL_VERTICAL))
9964 Feld[ax][ay] = EL_WALL;
9967 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9970 void MauerAblegerStahl(int ax, int ay)
9972 int element = Feld[ax][ay];
9973 int graphic = el2img(element);
9974 boolean oben_frei = FALSE, unten_frei = FALSE;
9975 boolean links_frei = FALSE, rechts_frei = FALSE;
9976 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9977 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9978 boolean new_wall = FALSE;
9980 if (IS_ANIMATED(graphic))
9981 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9983 if (!MovDelay[ax][ay]) /* start building new wall */
9984 MovDelay[ax][ay] = 6;
9986 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9989 if (MovDelay[ax][ay])
9993 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9995 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9997 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9999 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10000 rechts_frei = TRUE;
10002 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10003 element == EL_EXPANDABLE_STEELWALL_ANY)
10007 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10008 Store[ax][ay-1] = element;
10009 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10010 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10011 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10012 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10017 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10018 Store[ax][ay+1] = element;
10019 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10020 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10021 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10022 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10027 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10028 element == EL_EXPANDABLE_STEELWALL_ANY)
10032 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10033 Store[ax-1][ay] = element;
10034 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10035 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10036 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10037 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10043 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10044 Store[ax+1][ay] = element;
10045 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10046 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10047 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10048 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10053 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10054 oben_massiv = TRUE;
10055 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10056 unten_massiv = TRUE;
10057 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10058 links_massiv = TRUE;
10059 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10060 rechts_massiv = TRUE;
10062 if (((oben_massiv && unten_massiv) ||
10063 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10064 ((links_massiv && rechts_massiv) ||
10065 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10066 Feld[ax][ay] = EL_STEELWALL;
10069 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10072 void CheckForDragon(int x, int y)
10075 boolean dragon_found = FALSE;
10076 static int xy[4][2] =
10084 for (i = 0; i < NUM_DIRECTIONS; i++)
10086 for (j = 0; j < 4; j++)
10088 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10090 if (IN_LEV_FIELD(xx, yy) &&
10091 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10093 if (Feld[xx][yy] == EL_DRAGON)
10094 dragon_found = TRUE;
10103 for (i = 0; i < NUM_DIRECTIONS; i++)
10105 for (j = 0; j < 3; j++)
10107 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10109 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10111 Feld[xx][yy] = EL_EMPTY;
10112 TEST_DrawLevelField(xx, yy);
10121 static void InitBuggyBase(int x, int y)
10123 int element = Feld[x][y];
10124 int activating_delay = FRAMES_PER_SECOND / 4;
10126 ChangeDelay[x][y] =
10127 (element == EL_SP_BUGGY_BASE ?
10128 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10129 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10131 element == EL_SP_BUGGY_BASE_ACTIVE ?
10132 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10135 static void WarnBuggyBase(int x, int y)
10138 static int xy[4][2] =
10146 for (i = 0; i < NUM_DIRECTIONS; i++)
10148 int xx = x + xy[i][0];
10149 int yy = y + xy[i][1];
10151 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10153 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10160 static void InitTrap(int x, int y)
10162 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10165 static void ActivateTrap(int x, int y)
10167 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10170 static void ChangeActiveTrap(int x, int y)
10172 int graphic = IMG_TRAP_ACTIVE;
10174 /* if new animation frame was drawn, correct crumbled sand border */
10175 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10176 TEST_DrawLevelFieldCrumbledSand(x, y);
10179 static int getSpecialActionElement(int element, int number, int base_element)
10181 return (element != EL_EMPTY ? element :
10182 number != -1 ? base_element + number - 1 :
10186 static int getModifiedActionNumber(int value_old, int operator, int operand,
10187 int value_min, int value_max)
10189 int value_new = (operator == CA_MODE_SET ? operand :
10190 operator == CA_MODE_ADD ? value_old + operand :
10191 operator == CA_MODE_SUBTRACT ? value_old - operand :
10192 operator == CA_MODE_MULTIPLY ? value_old * operand :
10193 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10194 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10197 return (value_new < value_min ? value_min :
10198 value_new > value_max ? value_max :
10202 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10204 struct ElementInfo *ei = &element_info[element];
10205 struct ElementChangeInfo *change = &ei->change_page[page];
10206 int target_element = change->target_element;
10207 int action_type = change->action_type;
10208 int action_mode = change->action_mode;
10209 int action_arg = change->action_arg;
10210 int action_element = change->action_element;
10213 if (!change->has_action)
10216 /* ---------- determine action paramater values -------------------------- */
10218 int level_time_value =
10219 (level.time > 0 ? TimeLeft :
10222 int action_arg_element_raw =
10223 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10224 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10225 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10226 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10227 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10228 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10229 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10231 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10234 if (action_arg_element_raw == EL_GROUP_START)
10235 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10238 int action_arg_direction =
10239 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10240 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10241 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10242 change->actual_trigger_side :
10243 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10244 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10247 int action_arg_number_min =
10248 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10251 int action_arg_number_max =
10252 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10253 action_type == CA_SET_LEVEL_GEMS ? 999 :
10254 action_type == CA_SET_LEVEL_TIME ? 9999 :
10255 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10256 action_type == CA_SET_CE_VALUE ? 9999 :
10257 action_type == CA_SET_CE_SCORE ? 9999 :
10260 int action_arg_number_reset =
10261 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10262 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10263 action_type == CA_SET_LEVEL_TIME ? level.time :
10264 action_type == CA_SET_LEVEL_SCORE ? 0 :
10265 #if USE_NEW_CUSTOM_VALUE
10266 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10268 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10270 action_type == CA_SET_CE_SCORE ? 0 :
10273 int action_arg_number =
10274 (action_arg <= CA_ARG_MAX ? action_arg :
10275 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10276 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10277 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10278 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10279 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10280 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10281 #if USE_NEW_CUSTOM_VALUE
10282 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10284 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10286 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10287 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10288 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10289 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10290 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10291 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10292 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10293 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10294 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10295 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10296 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10297 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10298 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10299 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10302 int action_arg_number_old =
10303 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10304 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10305 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10306 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10307 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10310 int action_arg_number_new =
10311 getModifiedActionNumber(action_arg_number_old,
10312 action_mode, action_arg_number,
10313 action_arg_number_min, action_arg_number_max);
10316 int trigger_player_bits =
10317 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10318 change->actual_trigger_player_bits : change->trigger_player);
10320 int trigger_player_bits =
10321 (change->actual_trigger_player >= EL_PLAYER_1 &&
10322 change->actual_trigger_player <= EL_PLAYER_4 ?
10323 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10327 int action_arg_player_bits =
10328 (action_arg >= CA_ARG_PLAYER_1 &&
10329 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10330 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10331 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10334 /* ---------- execute action -------------------------------------------- */
10336 switch (action_type)
10343 /* ---------- level actions ------------------------------------------- */
10345 case CA_RESTART_LEVEL:
10347 game.restart_level = TRUE;
10352 case CA_SHOW_ENVELOPE:
10354 int element = getSpecialActionElement(action_arg_element,
10355 action_arg_number, EL_ENVELOPE_1);
10357 if (IS_ENVELOPE(element))
10358 local_player->show_envelope = element;
10363 case CA_SET_LEVEL_TIME:
10365 if (level.time > 0) /* only modify limited time value */
10367 TimeLeft = action_arg_number_new;
10370 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10372 DisplayGameControlValues();
10374 DrawGameValue_Time(TimeLeft);
10377 if (!TimeLeft && setup.time_limit)
10378 for (i = 0; i < MAX_PLAYERS; i++)
10379 KillPlayer(&stored_player[i]);
10385 case CA_SET_LEVEL_SCORE:
10387 local_player->score = action_arg_number_new;
10390 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10392 DisplayGameControlValues();
10394 DrawGameValue_Score(local_player->score);
10400 case CA_SET_LEVEL_GEMS:
10402 local_player->gems_still_needed = action_arg_number_new;
10405 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10407 DisplayGameControlValues();
10409 DrawGameValue_Emeralds(local_player->gems_still_needed);
10415 #if !USE_PLAYER_GRAVITY
10416 case CA_SET_LEVEL_GRAVITY:
10418 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10419 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10420 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10426 case CA_SET_LEVEL_WIND:
10428 game.wind_direction = action_arg_direction;
10433 /* ---------- player actions ------------------------------------------ */
10435 case CA_MOVE_PLAYER:
10437 /* automatically move to the next field in specified direction */
10438 for (i = 0; i < MAX_PLAYERS; i++)
10439 if (trigger_player_bits & (1 << i))
10440 stored_player[i].programmed_action = action_arg_direction;
10445 case CA_EXIT_PLAYER:
10447 for (i = 0; i < MAX_PLAYERS; i++)
10448 if (action_arg_player_bits & (1 << i))
10449 PlayerWins(&stored_player[i]);
10454 case CA_KILL_PLAYER:
10456 for (i = 0; i < MAX_PLAYERS; i++)
10457 if (action_arg_player_bits & (1 << i))
10458 KillPlayer(&stored_player[i]);
10463 case CA_SET_PLAYER_KEYS:
10465 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10466 int element = getSpecialActionElement(action_arg_element,
10467 action_arg_number, EL_KEY_1);
10469 if (IS_KEY(element))
10471 for (i = 0; i < MAX_PLAYERS; i++)
10473 if (trigger_player_bits & (1 << i))
10475 stored_player[i].key[KEY_NR(element)] = key_state;
10477 DrawGameDoorValues();
10485 case CA_SET_PLAYER_SPEED:
10488 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10491 for (i = 0; i < MAX_PLAYERS; i++)
10493 if (trigger_player_bits & (1 << i))
10495 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10497 if (action_arg == CA_ARG_SPEED_FASTER &&
10498 stored_player[i].cannot_move)
10500 action_arg_number = STEPSIZE_VERY_SLOW;
10502 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10503 action_arg == CA_ARG_SPEED_FASTER)
10505 action_arg_number = 2;
10506 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10509 else if (action_arg == CA_ARG_NUMBER_RESET)
10511 action_arg_number = level.initial_player_stepsize[i];
10515 getModifiedActionNumber(move_stepsize,
10518 action_arg_number_min,
10519 action_arg_number_max);
10521 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10528 case CA_SET_PLAYER_SHIELD:
10530 for (i = 0; i < MAX_PLAYERS; i++)
10532 if (trigger_player_bits & (1 << i))
10534 if (action_arg == CA_ARG_SHIELD_OFF)
10536 stored_player[i].shield_normal_time_left = 0;
10537 stored_player[i].shield_deadly_time_left = 0;
10539 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10541 stored_player[i].shield_normal_time_left = 999999;
10543 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10545 stored_player[i].shield_normal_time_left = 999999;
10546 stored_player[i].shield_deadly_time_left = 999999;
10554 #if USE_PLAYER_GRAVITY
10555 case CA_SET_PLAYER_GRAVITY:
10557 for (i = 0; i < MAX_PLAYERS; i++)
10559 if (trigger_player_bits & (1 << i))
10561 stored_player[i].gravity =
10562 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10563 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10564 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10565 stored_player[i].gravity);
10573 case CA_SET_PLAYER_ARTWORK:
10575 for (i = 0; i < MAX_PLAYERS; i++)
10577 if (trigger_player_bits & (1 << i))
10579 int artwork_element = action_arg_element;
10581 if (action_arg == CA_ARG_ELEMENT_RESET)
10583 (level.use_artwork_element[i] ? level.artwork_element[i] :
10584 stored_player[i].element_nr);
10586 #if USE_GFX_RESET_PLAYER_ARTWORK
10587 if (stored_player[i].artwork_element != artwork_element)
10588 stored_player[i].Frame = 0;
10591 stored_player[i].artwork_element = artwork_element;
10593 SetPlayerWaiting(&stored_player[i], FALSE);
10595 /* set number of special actions for bored and sleeping animation */
10596 stored_player[i].num_special_action_bored =
10597 get_num_special_action(artwork_element,
10598 ACTION_BORING_1, ACTION_BORING_LAST);
10599 stored_player[i].num_special_action_sleeping =
10600 get_num_special_action(artwork_element,
10601 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10608 case CA_SET_PLAYER_INVENTORY:
10610 for (i = 0; i < MAX_PLAYERS; i++)
10612 struct PlayerInfo *player = &stored_player[i];
10615 if (trigger_player_bits & (1 << i))
10617 int inventory_element = action_arg_element;
10619 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10620 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10621 action_arg == CA_ARG_ELEMENT_ACTION)
10623 int element = inventory_element;
10624 int collect_count = element_info[element].collect_count_initial;
10626 if (!IS_CUSTOM_ELEMENT(element))
10629 if (collect_count == 0)
10630 player->inventory_infinite_element = element;
10632 for (k = 0; k < collect_count; k++)
10633 if (player->inventory_size < MAX_INVENTORY_SIZE)
10634 player->inventory_element[player->inventory_size++] =
10637 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10638 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10639 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10641 if (player->inventory_infinite_element != EL_UNDEFINED &&
10642 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10643 action_arg_element_raw))
10644 player->inventory_infinite_element = EL_UNDEFINED;
10646 for (k = 0, j = 0; j < player->inventory_size; j++)
10648 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10649 action_arg_element_raw))
10650 player->inventory_element[k++] = player->inventory_element[j];
10653 player->inventory_size = k;
10655 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10657 if (player->inventory_size > 0)
10659 for (j = 0; j < player->inventory_size - 1; j++)
10660 player->inventory_element[j] = player->inventory_element[j + 1];
10662 player->inventory_size--;
10665 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10667 if (player->inventory_size > 0)
10668 player->inventory_size--;
10670 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10672 player->inventory_infinite_element = EL_UNDEFINED;
10673 player->inventory_size = 0;
10675 else if (action_arg == CA_ARG_INVENTORY_RESET)
10677 player->inventory_infinite_element = EL_UNDEFINED;
10678 player->inventory_size = 0;
10680 if (level.use_initial_inventory[i])
10682 for (j = 0; j < level.initial_inventory_size[i]; j++)
10684 int element = level.initial_inventory_content[i][j];
10685 int collect_count = element_info[element].collect_count_initial;
10687 if (!IS_CUSTOM_ELEMENT(element))
10690 if (collect_count == 0)
10691 player->inventory_infinite_element = element;
10693 for (k = 0; k < collect_count; k++)
10694 if (player->inventory_size < MAX_INVENTORY_SIZE)
10695 player->inventory_element[player->inventory_size++] =
10706 /* ---------- CE actions ---------------------------------------------- */
10708 case CA_SET_CE_VALUE:
10710 #if USE_NEW_CUSTOM_VALUE
10711 int last_ce_value = CustomValue[x][y];
10713 CustomValue[x][y] = action_arg_number_new;
10715 if (CustomValue[x][y] != last_ce_value)
10717 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10718 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10720 if (CustomValue[x][y] == 0)
10722 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10723 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10731 case CA_SET_CE_SCORE:
10733 #if USE_NEW_CUSTOM_VALUE
10734 int last_ce_score = ei->collect_score;
10736 ei->collect_score = action_arg_number_new;
10738 if (ei->collect_score != last_ce_score)
10740 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10741 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10743 if (ei->collect_score == 0)
10747 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10748 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10751 This is a very special case that seems to be a mixture between
10752 CheckElementChange() and CheckTriggeredElementChange(): while
10753 the first one only affects single elements that are triggered
10754 directly, the second one affects multiple elements in the playfield
10755 that are triggered indirectly by another element. This is a third
10756 case: Changing the CE score always affects multiple identical CEs,
10757 so every affected CE must be checked, not only the single CE for
10758 which the CE score was changed in the first place (as every instance
10759 of that CE shares the same CE score, and therefore also can change)!
10761 SCAN_PLAYFIELD(xx, yy)
10763 if (Feld[xx][yy] == element)
10764 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10765 CE_SCORE_GETS_ZERO);
10774 case CA_SET_CE_ARTWORK:
10776 int artwork_element = action_arg_element;
10777 boolean reset_frame = FALSE;
10780 if (action_arg == CA_ARG_ELEMENT_RESET)
10781 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10784 if (ei->gfx_element != artwork_element)
10785 reset_frame = TRUE;
10787 ei->gfx_element = artwork_element;
10789 SCAN_PLAYFIELD(xx, yy)
10791 if (Feld[xx][yy] == element)
10795 ResetGfxAnimation(xx, yy);
10796 ResetRandomAnimationValue(xx, yy);
10799 TEST_DrawLevelField(xx, yy);
10806 /* ---------- engine actions ------------------------------------------ */
10808 case CA_SET_ENGINE_SCAN_MODE:
10810 InitPlayfieldScanMode(action_arg);
10820 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10822 int old_element = Feld[x][y];
10823 int new_element = GetElementFromGroupElement(element);
10824 int previous_move_direction = MovDir[x][y];
10825 #if USE_NEW_CUSTOM_VALUE
10826 int last_ce_value = CustomValue[x][y];
10828 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10829 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10830 boolean add_player_onto_element = (new_element_is_player &&
10831 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10832 /* this breaks SnakeBite when a snake is
10833 halfway through a door that closes */
10834 /* NOW FIXED AT LEVEL INIT IN files.c */
10835 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10837 IS_WALKABLE(old_element));
10840 /* check if element under the player changes from accessible to unaccessible
10841 (needed for special case of dropping element which then changes) */
10842 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10843 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10851 if (!add_player_onto_element)
10853 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10854 RemoveMovingField(x, y);
10858 Feld[x][y] = new_element;
10860 #if !USE_GFX_RESET_GFX_ANIMATION
10861 ResetGfxAnimation(x, y);
10862 ResetRandomAnimationValue(x, y);
10865 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10866 MovDir[x][y] = previous_move_direction;
10868 #if USE_NEW_CUSTOM_VALUE
10869 if (element_info[new_element].use_last_ce_value)
10870 CustomValue[x][y] = last_ce_value;
10873 InitField_WithBug1(x, y, FALSE);
10875 new_element = Feld[x][y]; /* element may have changed */
10877 #if USE_GFX_RESET_GFX_ANIMATION
10878 ResetGfxAnimation(x, y);
10879 ResetRandomAnimationValue(x, y);
10882 TEST_DrawLevelField(x, y);
10884 if (GFX_CRUMBLED(new_element))
10885 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10889 /* check if element under the player changes from accessible to unaccessible
10890 (needed for special case of dropping element which then changes) */
10891 /* (must be checked after creating new element for walkable group elements) */
10892 #if USE_FIX_KILLED_BY_NON_WALKABLE
10893 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10894 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10901 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10902 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10911 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10912 if (new_element_is_player)
10913 RelocatePlayer(x, y, new_element);
10916 ChangeCount[x][y]++; /* count number of changes in the same frame */
10918 TestIfBadThingTouchesPlayer(x, y);
10919 TestIfPlayerTouchesCustomElement(x, y);
10920 TestIfElementTouchesCustomElement(x, y);
10923 static void CreateField(int x, int y, int element)
10925 CreateFieldExt(x, y, element, FALSE);
10928 static void CreateElementFromChange(int x, int y, int element)
10930 element = GET_VALID_RUNTIME_ELEMENT(element);
10932 #if USE_STOP_CHANGED_ELEMENTS
10933 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10935 int old_element = Feld[x][y];
10937 /* prevent changed element from moving in same engine frame
10938 unless both old and new element can either fall or move */
10939 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10940 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10945 CreateFieldExt(x, y, element, TRUE);
10948 static boolean ChangeElement(int x, int y, int element, int page)
10950 struct ElementInfo *ei = &element_info[element];
10951 struct ElementChangeInfo *change = &ei->change_page[page];
10952 int ce_value = CustomValue[x][y];
10953 int ce_score = ei->collect_score;
10954 int target_element;
10955 int old_element = Feld[x][y];
10957 /* always use default change event to prevent running into a loop */
10958 if (ChangeEvent[x][y] == -1)
10959 ChangeEvent[x][y] = CE_DELAY;
10961 if (ChangeEvent[x][y] == CE_DELAY)
10963 /* reset actual trigger element, trigger player and action element */
10964 change->actual_trigger_element = EL_EMPTY;
10965 change->actual_trigger_player = EL_EMPTY;
10966 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10967 change->actual_trigger_side = CH_SIDE_NONE;
10968 change->actual_trigger_ce_value = 0;
10969 change->actual_trigger_ce_score = 0;
10972 /* do not change elements more than a specified maximum number of changes */
10973 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10976 ChangeCount[x][y]++; /* count number of changes in the same frame */
10978 if (change->explode)
10985 if (change->use_target_content)
10987 boolean complete_replace = TRUE;
10988 boolean can_replace[3][3];
10991 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10994 boolean is_walkable;
10995 boolean is_diggable;
10996 boolean is_collectible;
10997 boolean is_removable;
10998 boolean is_destructible;
10999 int ex = x + xx - 1;
11000 int ey = y + yy - 1;
11001 int content_element = change->target_content.e[xx][yy];
11004 can_replace[xx][yy] = TRUE;
11006 if (ex == x && ey == y) /* do not check changing element itself */
11009 if (content_element == EL_EMPTY_SPACE)
11011 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11016 if (!IN_LEV_FIELD(ex, ey))
11018 can_replace[xx][yy] = FALSE;
11019 complete_replace = FALSE;
11026 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11027 e = MovingOrBlocked2Element(ex, ey);
11029 is_empty = (IS_FREE(ex, ey) ||
11030 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11032 is_walkable = (is_empty || IS_WALKABLE(e));
11033 is_diggable = (is_empty || IS_DIGGABLE(e));
11034 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11035 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11036 is_removable = (is_diggable || is_collectible);
11038 can_replace[xx][yy] =
11039 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11040 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11041 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11042 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11043 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11044 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11045 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11047 if (!can_replace[xx][yy])
11048 complete_replace = FALSE;
11051 if (!change->only_if_complete || complete_replace)
11053 boolean something_has_changed = FALSE;
11055 if (change->only_if_complete && change->use_random_replace &&
11056 RND(100) < change->random_percentage)
11059 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11061 int ex = x + xx - 1;
11062 int ey = y + yy - 1;
11063 int content_element;
11065 if (can_replace[xx][yy] && (!change->use_random_replace ||
11066 RND(100) < change->random_percentage))
11068 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11069 RemoveMovingField(ex, ey);
11071 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11073 content_element = change->target_content.e[xx][yy];
11074 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11075 ce_value, ce_score);
11077 CreateElementFromChange(ex, ey, target_element);
11079 something_has_changed = TRUE;
11081 /* for symmetry reasons, freeze newly created border elements */
11082 if (ex != x || ey != y)
11083 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11087 if (something_has_changed)
11089 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11090 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11096 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11097 ce_value, ce_score);
11099 if (element == EL_DIAGONAL_GROWING ||
11100 element == EL_DIAGONAL_SHRINKING)
11102 target_element = Store[x][y];
11104 Store[x][y] = EL_EMPTY;
11107 CreateElementFromChange(x, y, target_element);
11109 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11110 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11113 /* this uses direct change before indirect change */
11114 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11119 #if USE_NEW_DELAYED_ACTION
11121 static void HandleElementChange(int x, int y, int page)
11123 int element = MovingOrBlocked2Element(x, y);
11124 struct ElementInfo *ei = &element_info[element];
11125 struct ElementChangeInfo *change = &ei->change_page[page];
11128 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11129 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11132 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11133 x, y, element, element_info[element].token_name);
11134 printf("HandleElementChange(): This should never happen!\n");
11139 /* this can happen with classic bombs on walkable, changing elements */
11140 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11143 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11144 ChangeDelay[x][y] = 0;
11150 if (ChangeDelay[x][y] == 0) /* initialize element change */
11152 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11154 if (change->can_change)
11157 /* !!! not clear why graphic animation should be reset at all here !!! */
11158 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11159 #if USE_GFX_RESET_WHEN_NOT_MOVING
11160 /* when a custom element is about to change (for example by change delay),
11161 do not reset graphic animation when the custom element is moving */
11162 if (!IS_MOVING(x, y))
11165 ResetGfxAnimation(x, y);
11166 ResetRandomAnimationValue(x, y);
11170 if (change->pre_change_function)
11171 change->pre_change_function(x, y);
11175 ChangeDelay[x][y]--;
11177 if (ChangeDelay[x][y] != 0) /* continue element change */
11179 if (change->can_change)
11181 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11183 if (IS_ANIMATED(graphic))
11184 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11186 if (change->change_function)
11187 change->change_function(x, y);
11190 else /* finish element change */
11192 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11194 page = ChangePage[x][y];
11195 ChangePage[x][y] = -1;
11197 change = &ei->change_page[page];
11200 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11202 ChangeDelay[x][y] = 1; /* try change after next move step */
11203 ChangePage[x][y] = page; /* remember page to use for change */
11208 if (change->can_change)
11210 if (ChangeElement(x, y, element, page))
11212 if (change->post_change_function)
11213 change->post_change_function(x, y);
11217 if (change->has_action)
11218 ExecuteCustomElementAction(x, y, element, page);
11224 static void HandleElementChange(int x, int y, int page)
11226 int element = MovingOrBlocked2Element(x, y);
11227 struct ElementInfo *ei = &element_info[element];
11228 struct ElementChangeInfo *change = &ei->change_page[page];
11231 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11234 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11235 x, y, element, element_info[element].token_name);
11236 printf("HandleElementChange(): This should never happen!\n");
11241 /* this can happen with classic bombs on walkable, changing elements */
11242 if (!CAN_CHANGE(element))
11245 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11246 ChangeDelay[x][y] = 0;
11252 if (ChangeDelay[x][y] == 0) /* initialize element change */
11254 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11256 ResetGfxAnimation(x, y);
11257 ResetRandomAnimationValue(x, y);
11259 if (change->pre_change_function)
11260 change->pre_change_function(x, y);
11263 ChangeDelay[x][y]--;
11265 if (ChangeDelay[x][y] != 0) /* continue element change */
11267 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11269 if (IS_ANIMATED(graphic))
11270 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11272 if (change->change_function)
11273 change->change_function(x, y);
11275 else /* finish element change */
11277 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11279 page = ChangePage[x][y];
11280 ChangePage[x][y] = -1;
11282 change = &ei->change_page[page];
11285 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11287 ChangeDelay[x][y] = 1; /* try change after next move step */
11288 ChangePage[x][y] = page; /* remember page to use for change */
11293 if (ChangeElement(x, y, element, page))
11295 if (change->post_change_function)
11296 change->post_change_function(x, y);
11303 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11304 int trigger_element,
11306 int trigger_player,
11310 boolean change_done_any = FALSE;
11311 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11314 if (!(trigger_events[trigger_element][trigger_event]))
11318 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11319 trigger_event, recursion_loop_depth, recursion_loop_detected,
11320 recursion_loop_element, EL_NAME(recursion_loop_element));
11323 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11325 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11327 int element = EL_CUSTOM_START + i;
11328 boolean change_done = FALSE;
11331 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11332 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11335 for (p = 0; p < element_info[element].num_change_pages; p++)
11337 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11339 if (change->can_change_or_has_action &&
11340 change->has_event[trigger_event] &&
11341 change->trigger_side & trigger_side &&
11342 change->trigger_player & trigger_player &&
11343 change->trigger_page & trigger_page_bits &&
11344 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11346 change->actual_trigger_element = trigger_element;
11347 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11348 change->actual_trigger_player_bits = trigger_player;
11349 change->actual_trigger_side = trigger_side;
11350 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11351 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11354 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11355 element, EL_NAME(element), p);
11358 if ((change->can_change && !change_done) || change->has_action)
11362 SCAN_PLAYFIELD(x, y)
11364 if (Feld[x][y] == element)
11366 if (change->can_change && !change_done)
11368 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11369 /* if element already changed in this frame, not only prevent
11370 another element change (checked in ChangeElement()), but
11371 also prevent additional element actions for this element */
11373 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11374 !level.use_action_after_change_bug)
11379 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11380 element, EL_NAME(element), p);
11383 ChangeDelay[x][y] = 1;
11384 ChangeEvent[x][y] = trigger_event;
11386 HandleElementChange(x, y, p);
11388 #if USE_NEW_DELAYED_ACTION
11389 else if (change->has_action)
11391 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11392 /* if element already changed in this frame, not only prevent
11393 another element change (checked in ChangeElement()), but
11394 also prevent additional element actions for this element */
11396 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11397 !level.use_action_after_change_bug)
11403 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11404 element, EL_NAME(element), p);
11407 ExecuteCustomElementAction(x, y, element, p);
11408 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11411 if (change->has_action)
11413 ExecuteCustomElementAction(x, y, element, p);
11414 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11420 if (change->can_change)
11422 change_done = TRUE;
11423 change_done_any = TRUE;
11426 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11427 element, EL_NAME(element), p);
11436 RECURSION_LOOP_DETECTION_END();
11438 return change_done_any;
11441 static boolean CheckElementChangeExt(int x, int y,
11443 int trigger_element,
11445 int trigger_player,
11448 boolean change_done = FALSE;
11451 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11452 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11455 if (Feld[x][y] == EL_BLOCKED)
11457 Blocked2Moving(x, y, &x, &y);
11458 element = Feld[x][y];
11462 /* check if element has already changed */
11463 if (Feld[x][y] != element)
11466 /* check if element has already changed or is about to change after moving */
11467 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11468 Feld[x][y] != element) ||
11470 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11471 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11472 ChangePage[x][y] != -1)))
11477 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11478 trigger_event, recursion_loop_depth, recursion_loop_detected,
11479 recursion_loop_element, EL_NAME(recursion_loop_element));
11482 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11485 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11488 for (p = 0; p < element_info[element].num_change_pages; p++)
11490 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11492 /* check trigger element for all events where the element that is checked
11493 for changing interacts with a directly adjacent element -- this is
11494 different to element changes that affect other elements to change on the
11495 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11496 boolean check_trigger_element =
11497 (trigger_event == CE_TOUCHING_X ||
11498 trigger_event == CE_HITTING_X ||
11499 trigger_event == CE_HIT_BY_X ||
11501 /* this one was forgotten until 3.2.3 */
11502 trigger_event == CE_DIGGING_X);
11505 if (change->can_change_or_has_action &&
11506 change->has_event[trigger_event] &&
11507 change->trigger_side & trigger_side &&
11508 change->trigger_player & trigger_player &&
11509 (!check_trigger_element ||
11510 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11512 change->actual_trigger_element = trigger_element;
11513 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11514 change->actual_trigger_player_bits = trigger_player;
11515 change->actual_trigger_side = trigger_side;
11516 change->actual_trigger_ce_value = CustomValue[x][y];
11517 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11519 /* special case: trigger element not at (x,y) position for some events */
11520 if (check_trigger_element)
11532 { 0, 0 }, { 0, 0 }, { 0, 0 },
11536 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11537 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11539 change->actual_trigger_ce_value = CustomValue[xx][yy];
11540 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11543 if (change->can_change && !change_done)
11545 ChangeDelay[x][y] = 1;
11546 ChangeEvent[x][y] = trigger_event;
11548 HandleElementChange(x, y, p);
11550 change_done = TRUE;
11552 #if USE_NEW_DELAYED_ACTION
11553 else if (change->has_action)
11555 ExecuteCustomElementAction(x, y, element, p);
11556 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11559 if (change->has_action)
11561 ExecuteCustomElementAction(x, y, element, p);
11562 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11568 RECURSION_LOOP_DETECTION_END();
11570 return change_done;
11573 static void PlayPlayerSound(struct PlayerInfo *player)
11575 int jx = player->jx, jy = player->jy;
11576 int sound_element = player->artwork_element;
11577 int last_action = player->last_action_waiting;
11578 int action = player->action_waiting;
11580 if (player->is_waiting)
11582 if (action != last_action)
11583 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11585 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11589 if (action != last_action)
11590 StopSound(element_info[sound_element].sound[last_action]);
11592 if (last_action == ACTION_SLEEPING)
11593 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11597 static void PlayAllPlayersSound()
11601 for (i = 0; i < MAX_PLAYERS; i++)
11602 if (stored_player[i].active)
11603 PlayPlayerSound(&stored_player[i]);
11606 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11608 boolean last_waiting = player->is_waiting;
11609 int move_dir = player->MovDir;
11611 player->dir_waiting = move_dir;
11612 player->last_action_waiting = player->action_waiting;
11616 if (!last_waiting) /* not waiting -> waiting */
11618 player->is_waiting = TRUE;
11620 player->frame_counter_bored =
11622 game.player_boring_delay_fixed +
11623 GetSimpleRandom(game.player_boring_delay_random);
11624 player->frame_counter_sleeping =
11626 game.player_sleeping_delay_fixed +
11627 GetSimpleRandom(game.player_sleeping_delay_random);
11629 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11632 if (game.player_sleeping_delay_fixed +
11633 game.player_sleeping_delay_random > 0 &&
11634 player->anim_delay_counter == 0 &&
11635 player->post_delay_counter == 0 &&
11636 FrameCounter >= player->frame_counter_sleeping)
11637 player->is_sleeping = TRUE;
11638 else if (game.player_boring_delay_fixed +
11639 game.player_boring_delay_random > 0 &&
11640 FrameCounter >= player->frame_counter_bored)
11641 player->is_bored = TRUE;
11643 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11644 player->is_bored ? ACTION_BORING :
11647 if (player->is_sleeping && player->use_murphy)
11649 /* special case for sleeping Murphy when leaning against non-free tile */
11651 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11652 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11653 !IS_MOVING(player->jx - 1, player->jy)))
11654 move_dir = MV_LEFT;
11655 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11656 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11657 !IS_MOVING(player->jx + 1, player->jy)))
11658 move_dir = MV_RIGHT;
11660 player->is_sleeping = FALSE;
11662 player->dir_waiting = move_dir;
11665 if (player->is_sleeping)
11667 if (player->num_special_action_sleeping > 0)
11669 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11671 int last_special_action = player->special_action_sleeping;
11672 int num_special_action = player->num_special_action_sleeping;
11673 int special_action =
11674 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11675 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11676 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11677 last_special_action + 1 : ACTION_SLEEPING);
11678 int special_graphic =
11679 el_act_dir2img(player->artwork_element, special_action, move_dir);
11681 player->anim_delay_counter =
11682 graphic_info[special_graphic].anim_delay_fixed +
11683 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11684 player->post_delay_counter =
11685 graphic_info[special_graphic].post_delay_fixed +
11686 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11688 player->special_action_sleeping = special_action;
11691 if (player->anim_delay_counter > 0)
11693 player->action_waiting = player->special_action_sleeping;
11694 player->anim_delay_counter--;
11696 else if (player->post_delay_counter > 0)
11698 player->post_delay_counter--;
11702 else if (player->is_bored)
11704 if (player->num_special_action_bored > 0)
11706 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11708 int special_action =
11709 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11710 int special_graphic =
11711 el_act_dir2img(player->artwork_element, special_action, move_dir);
11713 player->anim_delay_counter =
11714 graphic_info[special_graphic].anim_delay_fixed +
11715 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11716 player->post_delay_counter =
11717 graphic_info[special_graphic].post_delay_fixed +
11718 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11720 player->special_action_bored = special_action;
11723 if (player->anim_delay_counter > 0)
11725 player->action_waiting = player->special_action_bored;
11726 player->anim_delay_counter--;
11728 else if (player->post_delay_counter > 0)
11730 player->post_delay_counter--;
11735 else if (last_waiting) /* waiting -> not waiting */
11737 player->is_waiting = FALSE;
11738 player->is_bored = FALSE;
11739 player->is_sleeping = FALSE;
11741 player->frame_counter_bored = -1;
11742 player->frame_counter_sleeping = -1;
11744 player->anim_delay_counter = 0;
11745 player->post_delay_counter = 0;
11747 player->dir_waiting = player->MovDir;
11748 player->action_waiting = ACTION_DEFAULT;
11750 player->special_action_bored = ACTION_DEFAULT;
11751 player->special_action_sleeping = ACTION_DEFAULT;
11755 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11757 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11758 int left = player_action & JOY_LEFT;
11759 int right = player_action & JOY_RIGHT;
11760 int up = player_action & JOY_UP;
11761 int down = player_action & JOY_DOWN;
11762 int button1 = player_action & JOY_BUTTON_1;
11763 int button2 = player_action & JOY_BUTTON_2;
11764 int dx = (left ? -1 : right ? 1 : 0);
11765 int dy = (up ? -1 : down ? 1 : 0);
11767 if (!player->active || tape.pausing)
11773 snapped = SnapField(player, dx, dy);
11777 dropped = DropElement(player);
11779 moved = MovePlayer(player, dx, dy);
11782 if (tape.single_step && tape.recording && !tape.pausing)
11784 if (button1 || (dropped && !moved))
11786 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11787 SnapField(player, 0, 0); /* stop snapping */
11791 SetPlayerWaiting(player, FALSE);
11793 return player_action;
11797 /* no actions for this player (no input at player's configured device) */
11799 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11800 SnapField(player, 0, 0);
11801 CheckGravityMovementWhenNotMoving(player);
11803 if (player->MovPos == 0)
11804 SetPlayerWaiting(player, TRUE);
11806 if (player->MovPos == 0) /* needed for tape.playing */
11807 player->is_moving = FALSE;
11809 player->is_dropping = FALSE;
11810 player->is_dropping_pressed = FALSE;
11811 player->drop_pressed_delay = 0;
11817 static void CheckLevelTime()
11821 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11823 if (level.native_em_level->lev->home == 0) /* all players at home */
11825 PlayerWins(local_player);
11827 AllPlayersGone = TRUE;
11829 level.native_em_level->lev->home = -1;
11832 if (level.native_em_level->ply[0]->alive == 0 &&
11833 level.native_em_level->ply[1]->alive == 0 &&
11834 level.native_em_level->ply[2]->alive == 0 &&
11835 level.native_em_level->ply[3]->alive == 0) /* all dead */
11836 AllPlayersGone = TRUE;
11839 if (TimeFrames >= FRAMES_PER_SECOND)
11844 for (i = 0; i < MAX_PLAYERS; i++)
11846 struct PlayerInfo *player = &stored_player[i];
11848 if (SHIELD_ON(player))
11850 player->shield_normal_time_left--;
11852 if (player->shield_deadly_time_left > 0)
11853 player->shield_deadly_time_left--;
11857 if (!local_player->LevelSolved && !level.use_step_counter)
11865 if (TimeLeft <= 10 && setup.time_limit)
11866 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11869 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11871 DisplayGameControlValues();
11873 DrawGameValue_Time(TimeLeft);
11876 if (!TimeLeft && setup.time_limit)
11878 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11879 level.native_em_level->lev->killed_out_of_time = TRUE;
11881 for (i = 0; i < MAX_PLAYERS; i++)
11882 KillPlayer(&stored_player[i]);
11886 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11888 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11890 DisplayGameControlValues();
11893 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11894 DrawGameValue_Time(TimePlayed);
11897 level.native_em_level->lev->time =
11898 (level.time == 0 ? TimePlayed : TimeLeft);
11901 if (tape.recording || tape.playing)
11902 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11906 UpdateAndDisplayGameControlValues();
11908 UpdateGameDoorValues();
11909 DrawGameDoorValues();
11913 void AdvanceFrameAndPlayerCounters(int player_nr)
11917 /* advance frame counters (global frame counter and time frame counter) */
11921 /* advance player counters (counters for move delay, move animation etc.) */
11922 for (i = 0; i < MAX_PLAYERS; i++)
11924 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11925 int move_delay_value = stored_player[i].move_delay_value;
11926 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11928 if (!advance_player_counters) /* not all players may be affected */
11931 #if USE_NEW_PLAYER_ANIM
11932 if (move_frames == 0) /* less than one move per game frame */
11934 int stepsize = TILEX / move_delay_value;
11935 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11936 int count = (stored_player[i].is_moving ?
11937 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11939 if (count % delay == 0)
11944 stored_player[i].Frame += move_frames;
11946 if (stored_player[i].MovPos != 0)
11947 stored_player[i].StepFrame += move_frames;
11949 if (stored_player[i].move_delay > 0)
11950 stored_player[i].move_delay--;
11952 /* due to bugs in previous versions, counter must count up, not down */
11953 if (stored_player[i].push_delay != -1)
11954 stored_player[i].push_delay++;
11956 if (stored_player[i].drop_delay > 0)
11957 stored_player[i].drop_delay--;
11959 if (stored_player[i].is_dropping_pressed)
11960 stored_player[i].drop_pressed_delay++;
11964 void StartGameActions(boolean init_network_game, boolean record_tape,
11967 unsigned long new_random_seed = InitRND(random_seed);
11970 TapeStartRecording(new_random_seed);
11972 #if defined(NETWORK_AVALIABLE)
11973 if (init_network_game)
11975 SendToServer_StartPlaying();
11986 static unsigned long game_frame_delay = 0;
11987 unsigned long game_frame_delay_value;
11988 byte *recorded_player_action;
11989 byte summarized_player_action = 0;
11990 byte tape_action[MAX_PLAYERS];
11993 /* detect endless loops, caused by custom element programming */
11994 if (recursion_loop_detected && recursion_loop_depth == 0)
11996 char *message = getStringCat3("Internal Error ! Element ",
11997 EL_NAME(recursion_loop_element),
11998 " caused endless loop ! Quit the game ?");
12000 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12001 EL_NAME(recursion_loop_element));
12003 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12005 recursion_loop_detected = FALSE; /* if game should be continued */
12012 if (game.restart_level)
12013 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
12015 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12017 if (level.native_em_level->lev->home == 0) /* all players at home */
12019 PlayerWins(local_player);
12021 AllPlayersGone = TRUE;
12023 level.native_em_level->lev->home = -1;
12026 if (level.native_em_level->ply[0]->alive == 0 &&
12027 level.native_em_level->ply[1]->alive == 0 &&
12028 level.native_em_level->ply[2]->alive == 0 &&
12029 level.native_em_level->ply[3]->alive == 0) /* all dead */
12030 AllPlayersGone = TRUE;
12033 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12036 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12039 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12042 game_frame_delay_value =
12043 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12045 if (tape.playing && tape.warp_forward && !tape.pausing)
12046 game_frame_delay_value = 0;
12048 /* ---------- main game synchronization point ---------- */
12050 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12052 if (network_playing && !network_player_action_received)
12054 /* try to get network player actions in time */
12056 #if defined(NETWORK_AVALIABLE)
12057 /* last chance to get network player actions without main loop delay */
12058 HandleNetworking();
12061 /* game was quit by network peer */
12062 if (game_status != GAME_MODE_PLAYING)
12065 if (!network_player_action_received)
12066 return; /* failed to get network player actions in time */
12068 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12074 /* at this point we know that we really continue executing the game */
12076 network_player_action_received = FALSE;
12078 /* when playing tape, read previously recorded player input from tape data */
12079 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12082 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12087 if (tape.set_centered_player)
12089 game.centered_player_nr_next = tape.centered_player_nr_next;
12090 game.set_centered_player = TRUE;
12093 for (i = 0; i < MAX_PLAYERS; i++)
12095 summarized_player_action |= stored_player[i].action;
12097 if (!network_playing)
12098 stored_player[i].effective_action = stored_player[i].action;
12101 #if defined(NETWORK_AVALIABLE)
12102 if (network_playing)
12103 SendToServer_MovePlayer(summarized_player_action);
12106 if (!options.network && !setup.team_mode)
12107 local_player->effective_action = summarized_player_action;
12109 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12111 for (i = 0; i < MAX_PLAYERS; i++)
12112 stored_player[i].effective_action =
12113 (i == game.centered_player_nr ? summarized_player_action : 0);
12116 if (recorded_player_action != NULL)
12117 for (i = 0; i < MAX_PLAYERS; i++)
12118 stored_player[i].effective_action = recorded_player_action[i];
12120 for (i = 0; i < MAX_PLAYERS; i++)
12122 tape_action[i] = stored_player[i].effective_action;
12124 /* (this can only happen in the R'n'D game engine) */
12125 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12126 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12129 /* only record actions from input devices, but not programmed actions */
12130 if (tape.recording)
12131 TapeRecordAction(tape_action);
12133 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12135 GameActions_EM_Main();
12143 void GameActions_EM_Main()
12145 byte effective_action[MAX_PLAYERS];
12146 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12149 for (i = 0; i < MAX_PLAYERS; i++)
12150 effective_action[i] = stored_player[i].effective_action;
12152 GameActions_EM(effective_action, warp_mode);
12156 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12159 void GameActions_RND()
12161 int magic_wall_x = 0, magic_wall_y = 0;
12162 int i, x, y, element, graphic;
12164 InitPlayfieldScanModeVars();
12166 #if USE_ONE_MORE_CHANGE_PER_FRAME
12167 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12169 SCAN_PLAYFIELD(x, y)
12171 ChangeCount[x][y] = 0;
12172 ChangeEvent[x][y] = -1;
12177 if (game.set_centered_player)
12179 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12181 /* switching to "all players" only possible if all players fit to screen */
12182 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12184 game.centered_player_nr_next = game.centered_player_nr;
12185 game.set_centered_player = FALSE;
12188 /* do not switch focus to non-existing (or non-active) player */
12189 if (game.centered_player_nr_next >= 0 &&
12190 !stored_player[game.centered_player_nr_next].active)
12192 game.centered_player_nr_next = game.centered_player_nr;
12193 game.set_centered_player = FALSE;
12197 if (game.set_centered_player &&
12198 ScreenMovPos == 0) /* screen currently aligned at tile position */
12202 if (game.centered_player_nr_next == -1)
12204 setScreenCenteredToAllPlayers(&sx, &sy);
12208 sx = stored_player[game.centered_player_nr_next].jx;
12209 sy = stored_player[game.centered_player_nr_next].jy;
12212 game.centered_player_nr = game.centered_player_nr_next;
12213 game.set_centered_player = FALSE;
12215 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12216 DrawGameDoorValues();
12219 for (i = 0; i < MAX_PLAYERS; i++)
12221 int actual_player_action = stored_player[i].effective_action;
12224 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12225 - rnd_equinox_tetrachloride 048
12226 - rnd_equinox_tetrachloride_ii 096
12227 - rnd_emanuel_schmieg 002
12228 - doctor_sloan_ww 001, 020
12230 if (stored_player[i].MovPos == 0)
12231 CheckGravityMovement(&stored_player[i]);
12234 /* overwrite programmed action with tape action */
12235 if (stored_player[i].programmed_action)
12236 actual_player_action = stored_player[i].programmed_action;
12238 PlayerActions(&stored_player[i], actual_player_action);
12240 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12243 ScrollScreen(NULL, SCROLL_GO_ON);
12245 /* for backwards compatibility, the following code emulates a fixed bug that
12246 occured when pushing elements (causing elements that just made their last
12247 pushing step to already (if possible) make their first falling step in the
12248 same game frame, which is bad); this code is also needed to use the famous
12249 "spring push bug" which is used in older levels and might be wanted to be
12250 used also in newer levels, but in this case the buggy pushing code is only
12251 affecting the "spring" element and no other elements */
12253 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12255 for (i = 0; i < MAX_PLAYERS; i++)
12257 struct PlayerInfo *player = &stored_player[i];
12258 int x = player->jx;
12259 int y = player->jy;
12261 if (player->active && player->is_pushing && player->is_moving &&
12263 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12264 Feld[x][y] == EL_SPRING))
12266 ContinueMoving(x, y);
12268 /* continue moving after pushing (this is actually a bug) */
12269 if (!IS_MOVING(x, y))
12270 Stop[x][y] = FALSE;
12276 debug_print_timestamp(0, "start main loop profiling");
12279 SCAN_PLAYFIELD(x, y)
12281 ChangeCount[x][y] = 0;
12282 ChangeEvent[x][y] = -1;
12284 /* this must be handled before main playfield loop */
12285 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12288 if (MovDelay[x][y] <= 0)
12292 #if USE_NEW_SNAP_DELAY
12293 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12296 if (MovDelay[x][y] <= 0)
12299 TEST_DrawLevelField(x, y);
12301 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12307 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12309 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12310 printf("GameActions(): This should never happen!\n");
12312 ChangePage[x][y] = -1;
12316 Stop[x][y] = FALSE;
12317 if (WasJustMoving[x][y] > 0)
12318 WasJustMoving[x][y]--;
12319 if (WasJustFalling[x][y] > 0)
12320 WasJustFalling[x][y]--;
12321 if (CheckCollision[x][y] > 0)
12322 CheckCollision[x][y]--;
12323 if (CheckImpact[x][y] > 0)
12324 CheckImpact[x][y]--;
12328 /* reset finished pushing action (not done in ContinueMoving() to allow
12329 continuous pushing animation for elements with zero push delay) */
12330 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12332 ResetGfxAnimation(x, y);
12333 TEST_DrawLevelField(x, y);
12337 if (IS_BLOCKED(x, y))
12341 Blocked2Moving(x, y, &oldx, &oldy);
12342 if (!IS_MOVING(oldx, oldy))
12344 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12345 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12346 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12347 printf("GameActions(): This should never happen!\n");
12354 debug_print_timestamp(0, "- time for pre-main loop:");
12357 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12358 SCAN_PLAYFIELD(x, y)
12360 element = Feld[x][y];
12361 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12366 int element2 = element;
12367 int graphic2 = graphic;
12369 int element2 = Feld[x][y];
12370 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12372 int last_gfx_frame = GfxFrame[x][y];
12374 if (graphic_info[graphic2].anim_global_sync)
12375 GfxFrame[x][y] = FrameCounter;
12376 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12377 GfxFrame[x][y] = CustomValue[x][y];
12378 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12379 GfxFrame[x][y] = element_info[element2].collect_score;
12380 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12381 GfxFrame[x][y] = ChangeDelay[x][y];
12383 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12384 DrawLevelGraphicAnimation(x, y, graphic2);
12387 ResetGfxFrame(x, y, TRUE);
12391 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12392 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12393 ResetRandomAnimationValue(x, y);
12397 SetRandomAnimationValue(x, y);
12401 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12404 #endif // -------------------- !!! TEST ONLY !!! --------------------
12407 debug_print_timestamp(0, "- time for TEST loop: -->");
12410 SCAN_PLAYFIELD(x, y)
12412 element = Feld[x][y];
12413 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12415 ResetGfxFrame(x, y, TRUE);
12417 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12418 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12419 ResetRandomAnimationValue(x, y);
12421 SetRandomAnimationValue(x, y);
12423 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12425 if (IS_INACTIVE(element))
12427 if (IS_ANIMATED(graphic))
12428 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12433 /* this may take place after moving, so 'element' may have changed */
12434 if (IS_CHANGING(x, y) &&
12435 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12437 int page = element_info[element].event_page_nr[CE_DELAY];
12440 HandleElementChange(x, y, page);
12442 if (CAN_CHANGE(element))
12443 HandleElementChange(x, y, page);
12445 if (HAS_ACTION(element))
12446 ExecuteCustomElementAction(x, y, element, page);
12449 element = Feld[x][y];
12450 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12453 #if 0 // ---------------------------------------------------------------------
12455 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12459 element = Feld[x][y];
12460 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12462 if (IS_ANIMATED(graphic) &&
12463 !IS_MOVING(x, y) &&
12465 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12467 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12468 TEST_DrawTwinkleOnField(x, y);
12470 else if (IS_MOVING(x, y))
12471 ContinueMoving(x, y);
12478 case EL_EM_EXIT_OPEN:
12479 case EL_SP_EXIT_OPEN:
12480 case EL_STEEL_EXIT_OPEN:
12481 case EL_EM_STEEL_EXIT_OPEN:
12482 case EL_SP_TERMINAL:
12483 case EL_SP_TERMINAL_ACTIVE:
12484 case EL_EXTRA_TIME:
12485 case EL_SHIELD_NORMAL:
12486 case EL_SHIELD_DEADLY:
12487 if (IS_ANIMATED(graphic))
12488 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12491 case EL_DYNAMITE_ACTIVE:
12492 case EL_EM_DYNAMITE_ACTIVE:
12493 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12494 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12495 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12496 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12497 case EL_SP_DISK_RED_ACTIVE:
12498 CheckDynamite(x, y);
12501 case EL_AMOEBA_GROWING:
12502 AmoebeWaechst(x, y);
12505 case EL_AMOEBA_SHRINKING:
12506 AmoebaDisappearing(x, y);
12509 #if !USE_NEW_AMOEBA_CODE
12510 case EL_AMOEBA_WET:
12511 case EL_AMOEBA_DRY:
12512 case EL_AMOEBA_FULL:
12514 case EL_EMC_DRIPPER:
12515 AmoebeAbleger(x, y);
12519 case EL_GAME_OF_LIFE:
12524 case EL_EXIT_CLOSED:
12528 case EL_EM_EXIT_CLOSED:
12532 case EL_STEEL_EXIT_CLOSED:
12533 CheckExitSteel(x, y);
12536 case EL_EM_STEEL_EXIT_CLOSED:
12537 CheckExitSteelEM(x, y);
12540 case EL_SP_EXIT_CLOSED:
12544 case EL_EXPANDABLE_WALL_GROWING:
12545 case EL_EXPANDABLE_STEELWALL_GROWING:
12546 MauerWaechst(x, y);
12549 case EL_EXPANDABLE_WALL:
12550 case EL_EXPANDABLE_WALL_HORIZONTAL:
12551 case EL_EXPANDABLE_WALL_VERTICAL:
12552 case EL_EXPANDABLE_WALL_ANY:
12553 case EL_BD_EXPANDABLE_WALL:
12554 MauerAbleger(x, y);
12557 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12558 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12559 case EL_EXPANDABLE_STEELWALL_ANY:
12560 MauerAblegerStahl(x, y);
12564 CheckForDragon(x, y);
12570 case EL_ELEMENT_SNAPPING:
12571 case EL_DIAGONAL_SHRINKING:
12572 case EL_DIAGONAL_GROWING:
12575 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12577 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12582 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12583 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12588 #else // ---------------------------------------------------------------------
12590 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12594 element = Feld[x][y];
12595 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12597 if (IS_ANIMATED(graphic) &&
12598 !IS_MOVING(x, y) &&
12600 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12602 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12603 TEST_DrawTwinkleOnField(x, y);
12605 else if ((element == EL_ACID ||
12606 element == EL_EXIT_OPEN ||
12607 element == EL_EM_EXIT_OPEN ||
12608 element == EL_SP_EXIT_OPEN ||
12609 element == EL_STEEL_EXIT_OPEN ||
12610 element == EL_EM_STEEL_EXIT_OPEN ||
12611 element == EL_SP_TERMINAL ||
12612 element == EL_SP_TERMINAL_ACTIVE ||
12613 element == EL_EXTRA_TIME ||
12614 element == EL_SHIELD_NORMAL ||
12615 element == EL_SHIELD_DEADLY) &&
12616 IS_ANIMATED(graphic))
12617 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12618 else if (IS_MOVING(x, y))
12619 ContinueMoving(x, y);
12620 else if (IS_ACTIVE_BOMB(element))
12621 CheckDynamite(x, y);
12622 else if (element == EL_AMOEBA_GROWING)
12623 AmoebeWaechst(x, y);
12624 else if (element == EL_AMOEBA_SHRINKING)
12625 AmoebaDisappearing(x, y);
12627 #if !USE_NEW_AMOEBA_CODE
12628 else if (IS_AMOEBALIVE(element))
12629 AmoebeAbleger(x, y);
12632 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12634 else if (element == EL_EXIT_CLOSED)
12636 else if (element == EL_EM_EXIT_CLOSED)
12638 else if (element == EL_STEEL_EXIT_CLOSED)
12639 CheckExitSteel(x, y);
12640 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12641 CheckExitSteelEM(x, y);
12642 else if (element == EL_SP_EXIT_CLOSED)
12644 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12645 element == EL_EXPANDABLE_STEELWALL_GROWING)
12646 MauerWaechst(x, y);
12647 else if (element == EL_EXPANDABLE_WALL ||
12648 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12649 element == EL_EXPANDABLE_WALL_VERTICAL ||
12650 element == EL_EXPANDABLE_WALL_ANY ||
12651 element == EL_BD_EXPANDABLE_WALL)
12652 MauerAbleger(x, y);
12653 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12654 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12655 element == EL_EXPANDABLE_STEELWALL_ANY)
12656 MauerAblegerStahl(x, y);
12657 else if (element == EL_FLAMES)
12658 CheckForDragon(x, y);
12659 else if (element == EL_EXPLOSION)
12660 ; /* drawing of correct explosion animation is handled separately */
12661 else if (element == EL_ELEMENT_SNAPPING ||
12662 element == EL_DIAGONAL_SHRINKING ||
12663 element == EL_DIAGONAL_GROWING)
12665 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12667 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12669 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12670 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12672 #endif // ---------------------------------------------------------------------
12674 if (IS_BELT_ACTIVE(element))
12675 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12677 if (game.magic_wall_active)
12679 int jx = local_player->jx, jy = local_player->jy;
12681 /* play the element sound at the position nearest to the player */
12682 if ((element == EL_MAGIC_WALL_FULL ||
12683 element == EL_MAGIC_WALL_ACTIVE ||
12684 element == EL_MAGIC_WALL_EMPTYING ||
12685 element == EL_BD_MAGIC_WALL_FULL ||
12686 element == EL_BD_MAGIC_WALL_ACTIVE ||
12687 element == EL_BD_MAGIC_WALL_EMPTYING ||
12688 element == EL_DC_MAGIC_WALL_FULL ||
12689 element == EL_DC_MAGIC_WALL_ACTIVE ||
12690 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12691 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12700 debug_print_timestamp(0, "- time for MAIN loop: -->");
12703 #if USE_NEW_AMOEBA_CODE
12704 /* new experimental amoeba growth stuff */
12705 if (!(FrameCounter % 8))
12707 static unsigned long random = 1684108901;
12709 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12711 x = RND(lev_fieldx);
12712 y = RND(lev_fieldy);
12713 element = Feld[x][y];
12715 if (!IS_PLAYER(x,y) &&
12716 (element == EL_EMPTY ||
12717 CAN_GROW_INTO(element) ||
12718 element == EL_QUICKSAND_EMPTY ||
12719 element == EL_QUICKSAND_FAST_EMPTY ||
12720 element == EL_ACID_SPLASH_LEFT ||
12721 element == EL_ACID_SPLASH_RIGHT))
12723 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12724 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12725 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12726 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12727 Feld[x][y] = EL_AMOEBA_DROP;
12730 random = random * 129 + 1;
12736 if (game.explosions_delayed)
12739 game.explosions_delayed = FALSE;
12741 SCAN_PLAYFIELD(x, y)
12743 element = Feld[x][y];
12745 if (ExplodeField[x][y])
12746 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12747 else if (element == EL_EXPLOSION)
12748 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12750 ExplodeField[x][y] = EX_TYPE_NONE;
12753 game.explosions_delayed = TRUE;
12756 if (game.magic_wall_active)
12758 if (!(game.magic_wall_time_left % 4))
12760 int element = Feld[magic_wall_x][magic_wall_y];
12762 if (element == EL_BD_MAGIC_WALL_FULL ||
12763 element == EL_BD_MAGIC_WALL_ACTIVE ||
12764 element == EL_BD_MAGIC_WALL_EMPTYING)
12765 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12766 else if (element == EL_DC_MAGIC_WALL_FULL ||
12767 element == EL_DC_MAGIC_WALL_ACTIVE ||
12768 element == EL_DC_MAGIC_WALL_EMPTYING)
12769 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12771 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12774 if (game.magic_wall_time_left > 0)
12776 game.magic_wall_time_left--;
12778 if (!game.magic_wall_time_left)
12780 SCAN_PLAYFIELD(x, y)
12782 element = Feld[x][y];
12784 if (element == EL_MAGIC_WALL_ACTIVE ||
12785 element == EL_MAGIC_WALL_FULL)
12787 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12788 TEST_DrawLevelField(x, y);
12790 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12791 element == EL_BD_MAGIC_WALL_FULL)
12793 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12794 TEST_DrawLevelField(x, y);
12796 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12797 element == EL_DC_MAGIC_WALL_FULL)
12799 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12800 TEST_DrawLevelField(x, y);
12804 game.magic_wall_active = FALSE;
12809 if (game.light_time_left > 0)
12811 game.light_time_left--;
12813 if (game.light_time_left == 0)
12814 RedrawAllLightSwitchesAndInvisibleElements();
12817 if (game.timegate_time_left > 0)
12819 game.timegate_time_left--;
12821 if (game.timegate_time_left == 0)
12822 CloseAllOpenTimegates();
12825 if (game.lenses_time_left > 0)
12827 game.lenses_time_left--;
12829 if (game.lenses_time_left == 0)
12830 RedrawAllInvisibleElementsForLenses();
12833 if (game.magnify_time_left > 0)
12835 game.magnify_time_left--;
12837 if (game.magnify_time_left == 0)
12838 RedrawAllInvisibleElementsForMagnifier();
12841 for (i = 0; i < MAX_PLAYERS; i++)
12843 struct PlayerInfo *player = &stored_player[i];
12845 if (SHIELD_ON(player))
12847 if (player->shield_deadly_time_left)
12848 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12849 else if (player->shield_normal_time_left)
12850 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12854 #if USE_DELAYED_GFX_REDRAW
12855 SCAN_PLAYFIELD(x, y)
12858 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12860 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12861 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12864 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12865 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12867 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12868 DrawLevelField(x, y);
12870 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12871 DrawLevelFieldCrumbledSand(x, y);
12873 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12874 DrawLevelFieldCrumbledSandNeighbours(x, y);
12876 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12877 DrawTwinkleOnField(x, y);
12880 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12887 PlayAllPlayersSound();
12889 if (options.debug) /* calculate frames per second */
12891 static unsigned long fps_counter = 0;
12892 static int fps_frames = 0;
12893 unsigned long fps_delay_ms = Counter() - fps_counter;
12897 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12899 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12902 fps_counter = Counter();
12905 redraw_mask |= REDRAW_FPS;
12908 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12910 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12912 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12914 local_player->show_envelope = 0;
12918 debug_print_timestamp(0, "stop main loop profiling ");
12919 printf("----------------------------------------------------------\n");
12922 /* use random number generator in every frame to make it less predictable */
12923 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12927 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12929 int min_x = x, min_y = y, max_x = x, max_y = y;
12932 for (i = 0; i < MAX_PLAYERS; i++)
12934 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12936 if (!stored_player[i].active || &stored_player[i] == player)
12939 min_x = MIN(min_x, jx);
12940 min_y = MIN(min_y, jy);
12941 max_x = MAX(max_x, jx);
12942 max_y = MAX(max_y, jy);
12945 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12948 static boolean AllPlayersInVisibleScreen()
12952 for (i = 0; i < MAX_PLAYERS; i++)
12954 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12956 if (!stored_player[i].active)
12959 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12966 void ScrollLevel(int dx, int dy)
12969 /* (directly solved in BlitBitmap() now) */
12970 static Bitmap *bitmap_db_field2 = NULL;
12971 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12978 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12979 /* only horizontal XOR vertical scroll direction allowed */
12980 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12985 /* (directly solved in BlitBitmap() now) */
12986 if (bitmap_db_field2 == NULL)
12987 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12989 /* needed when blitting directly to same bitmap -- should not be needed with
12990 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12991 BlitBitmap(drawto_field, bitmap_db_field2,
12992 FX + TILEX * (dx == -1) - softscroll_offset,
12993 FY + TILEY * (dy == -1) - softscroll_offset,
12994 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12995 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12996 FX + TILEX * (dx == 1) - softscroll_offset,
12997 FY + TILEY * (dy == 1) - softscroll_offset);
12998 BlitBitmap(bitmap_db_field2, drawto_field,
12999 FX + TILEX * (dx == 1) - softscroll_offset,
13000 FY + TILEY * (dy == 1) - softscroll_offset,
13001 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13002 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13003 FX + TILEX * (dx == 1) - softscroll_offset,
13004 FY + TILEY * (dy == 1) - softscroll_offset);
13009 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13010 int xsize = (BX2 - BX1 + 1);
13011 int ysize = (BY2 - BY1 + 1);
13012 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13013 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13014 int step = (start < end ? +1 : -1);
13016 for (i = start; i != end; i += step)
13018 BlitBitmap(drawto_field, drawto_field,
13019 FX + TILEX * (dx != 0 ? i + step : 0),
13020 FY + TILEY * (dy != 0 ? i + step : 0),
13021 TILEX * (dx != 0 ? 1 : xsize),
13022 TILEY * (dy != 0 ? 1 : ysize),
13023 FX + TILEX * (dx != 0 ? i : 0),
13024 FY + TILEY * (dy != 0 ? i : 0));
13029 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13031 BlitBitmap(drawto_field, drawto_field,
13032 FX + TILEX * (dx == -1) - softscroll_offset,
13033 FY + TILEY * (dy == -1) - softscroll_offset,
13034 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13035 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13036 FX + TILEX * (dx == 1) - softscroll_offset,
13037 FY + TILEY * (dy == 1) - softscroll_offset);
13043 x = (dx == 1 ? BX1 : BX2);
13044 for (y = BY1; y <= BY2; y++)
13045 DrawScreenField(x, y);
13050 y = (dy == 1 ? BY1 : BY2);
13051 for (x = BX1; x <= BX2; x++)
13052 DrawScreenField(x, y);
13055 redraw_mask |= REDRAW_FIELD;
13058 static boolean canFallDown(struct PlayerInfo *player)
13060 int jx = player->jx, jy = player->jy;
13062 return (IN_LEV_FIELD(jx, jy + 1) &&
13063 (IS_FREE(jx, jy + 1) ||
13064 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13065 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13066 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13069 static boolean canPassField(int x, int y, int move_dir)
13071 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13072 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13073 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13074 int nextx = x + dx;
13075 int nexty = y + dy;
13076 int element = Feld[x][y];
13078 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13079 !CAN_MOVE(element) &&
13080 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13081 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13082 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13085 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13087 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13088 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13089 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13093 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13094 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13095 (IS_DIGGABLE(Feld[newx][newy]) ||
13096 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13097 canPassField(newx, newy, move_dir)));
13100 static void CheckGravityMovement(struct PlayerInfo *player)
13102 #if USE_PLAYER_GRAVITY
13103 if (player->gravity && !player->programmed_action)
13105 if (game.gravity && !player->programmed_action)
13108 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13109 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13110 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13111 int jx = player->jx, jy = player->jy;
13112 boolean player_is_moving_to_valid_field =
13113 (!player_is_snapping &&
13114 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13115 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13116 boolean player_can_fall_down = canFallDown(player);
13118 if (player_can_fall_down &&
13119 !player_is_moving_to_valid_field)
13120 player->programmed_action = MV_DOWN;
13124 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13126 return CheckGravityMovement(player);
13128 #if USE_PLAYER_GRAVITY
13129 if (player->gravity && !player->programmed_action)
13131 if (game.gravity && !player->programmed_action)
13134 int jx = player->jx, jy = player->jy;
13135 boolean field_under_player_is_free =
13136 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13137 boolean player_is_standing_on_valid_field =
13138 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13139 (IS_WALKABLE(Feld[jx][jy]) &&
13140 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13142 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13143 player->programmed_action = MV_DOWN;
13148 MovePlayerOneStep()
13149 -----------------------------------------------------------------------------
13150 dx, dy: direction (non-diagonal) to try to move the player to
13151 real_dx, real_dy: direction as read from input device (can be diagonal)
13154 boolean MovePlayerOneStep(struct PlayerInfo *player,
13155 int dx, int dy, int real_dx, int real_dy)
13157 int jx = player->jx, jy = player->jy;
13158 int new_jx = jx + dx, new_jy = jy + dy;
13159 #if !USE_FIXED_DONT_RUN_INTO
13163 boolean player_can_move = !player->cannot_move;
13165 if (!player->active || (!dx && !dy))
13166 return MP_NO_ACTION;
13168 player->MovDir = (dx < 0 ? MV_LEFT :
13169 dx > 0 ? MV_RIGHT :
13171 dy > 0 ? MV_DOWN : MV_NONE);
13173 if (!IN_LEV_FIELD(new_jx, new_jy))
13174 return MP_NO_ACTION;
13176 if (!player_can_move)
13178 if (player->MovPos == 0)
13180 player->is_moving = FALSE;
13181 player->is_digging = FALSE;
13182 player->is_collecting = FALSE;
13183 player->is_snapping = FALSE;
13184 player->is_pushing = FALSE;
13189 if (!options.network && game.centered_player_nr == -1 &&
13190 !AllPlayersInSight(player, new_jx, new_jy))
13191 return MP_NO_ACTION;
13193 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13194 return MP_NO_ACTION;
13197 #if !USE_FIXED_DONT_RUN_INTO
13198 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13200 /* (moved to DigField()) */
13201 if (player_can_move && DONT_RUN_INTO(element))
13203 if (element == EL_ACID && dx == 0 && dy == 1)
13205 SplashAcid(new_jx, new_jy);
13206 Feld[jx][jy] = EL_PLAYER_1;
13207 InitMovingField(jx, jy, MV_DOWN);
13208 Store[jx][jy] = EL_ACID;
13209 ContinueMoving(jx, jy);
13210 BuryPlayer(player);
13213 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13219 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13220 if (can_move != MP_MOVING)
13223 /* check if DigField() has caused relocation of the player */
13224 if (player->jx != jx || player->jy != jy)
13225 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13227 StorePlayer[jx][jy] = 0;
13228 player->last_jx = jx;
13229 player->last_jy = jy;
13230 player->jx = new_jx;
13231 player->jy = new_jy;
13232 StorePlayer[new_jx][new_jy] = player->element_nr;
13234 if (player->move_delay_value_next != -1)
13236 player->move_delay_value = player->move_delay_value_next;
13237 player->move_delay_value_next = -1;
13241 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13243 player->step_counter++;
13245 PlayerVisit[jx][jy] = FrameCounter;
13247 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13248 player->is_moving = TRUE;
13252 /* should better be called in MovePlayer(), but this breaks some tapes */
13253 ScrollPlayer(player, SCROLL_INIT);
13259 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13261 int jx = player->jx, jy = player->jy;
13262 int old_jx = jx, old_jy = jy;
13263 int moved = MP_NO_ACTION;
13265 if (!player->active)
13270 if (player->MovPos == 0)
13272 player->is_moving = FALSE;
13273 player->is_digging = FALSE;
13274 player->is_collecting = FALSE;
13275 player->is_snapping = FALSE;
13276 player->is_pushing = FALSE;
13282 if (player->move_delay > 0)
13285 player->move_delay = -1; /* set to "uninitialized" value */
13287 /* store if player is automatically moved to next field */
13288 player->is_auto_moving = (player->programmed_action != MV_NONE);
13290 /* remove the last programmed player action */
13291 player->programmed_action = 0;
13293 if (player->MovPos)
13295 /* should only happen if pre-1.2 tape recordings are played */
13296 /* this is only for backward compatibility */
13298 int original_move_delay_value = player->move_delay_value;
13301 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13305 /* scroll remaining steps with finest movement resolution */
13306 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13308 while (player->MovPos)
13310 ScrollPlayer(player, SCROLL_GO_ON);
13311 ScrollScreen(NULL, SCROLL_GO_ON);
13313 AdvanceFrameAndPlayerCounters(player->index_nr);
13319 player->move_delay_value = original_move_delay_value;
13322 player->is_active = FALSE;
13324 if (player->last_move_dir & MV_HORIZONTAL)
13326 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13327 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13331 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13332 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13335 #if USE_FIXED_BORDER_RUNNING_GFX
13336 if (!moved && !player->is_active)
13338 player->is_moving = FALSE;
13339 player->is_digging = FALSE;
13340 player->is_collecting = FALSE;
13341 player->is_snapping = FALSE;
13342 player->is_pushing = FALSE;
13350 if (moved & MP_MOVING && !ScreenMovPos &&
13351 (player->index_nr == game.centered_player_nr ||
13352 game.centered_player_nr == -1))
13354 if (moved & MP_MOVING && !ScreenMovPos &&
13355 (player == local_player || !options.network))
13358 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13359 int offset = game.scroll_delay_value;
13361 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13363 /* actual player has left the screen -- scroll in that direction */
13364 if (jx != old_jx) /* player has moved horizontally */
13365 scroll_x += (jx - old_jx);
13366 else /* player has moved vertically */
13367 scroll_y += (jy - old_jy);
13371 if (jx != old_jx) /* player has moved horizontally */
13373 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13374 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13375 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13377 /* don't scroll over playfield boundaries */
13378 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13379 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13381 /* don't scroll more than one field at a time */
13382 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13384 /* don't scroll against the player's moving direction */
13385 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13386 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13387 scroll_x = old_scroll_x;
13389 else /* player has moved vertically */
13391 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13392 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13393 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13395 /* don't scroll over playfield boundaries */
13396 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13397 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13399 /* don't scroll more than one field at a time */
13400 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13402 /* don't scroll against the player's moving direction */
13403 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13404 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13405 scroll_y = old_scroll_y;
13409 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13412 if (!options.network && game.centered_player_nr == -1 &&
13413 !AllPlayersInVisibleScreen())
13415 scroll_x = old_scroll_x;
13416 scroll_y = old_scroll_y;
13420 if (!options.network && !AllPlayersInVisibleScreen())
13422 scroll_x = old_scroll_x;
13423 scroll_y = old_scroll_y;
13428 ScrollScreen(player, SCROLL_INIT);
13429 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13434 player->StepFrame = 0;
13436 if (moved & MP_MOVING)
13438 if (old_jx != jx && old_jy == jy)
13439 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13440 else if (old_jx == jx && old_jy != jy)
13441 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13443 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13445 player->last_move_dir = player->MovDir;
13446 player->is_moving = TRUE;
13447 player->is_snapping = FALSE;
13448 player->is_switching = FALSE;
13449 player->is_dropping = FALSE;
13450 player->is_dropping_pressed = FALSE;
13451 player->drop_pressed_delay = 0;
13454 /* should better be called here than above, but this breaks some tapes */
13455 ScrollPlayer(player, SCROLL_INIT);
13460 CheckGravityMovementWhenNotMoving(player);
13462 player->is_moving = FALSE;
13464 /* at this point, the player is allowed to move, but cannot move right now
13465 (e.g. because of something blocking the way) -- ensure that the player
13466 is also allowed to move in the next frame (in old versions before 3.1.1,
13467 the player was forced to wait again for eight frames before next try) */
13469 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13470 player->move_delay = 0; /* allow direct movement in the next frame */
13473 if (player->move_delay == -1) /* not yet initialized by DigField() */
13474 player->move_delay = player->move_delay_value;
13476 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13478 TestIfPlayerTouchesBadThing(jx, jy);
13479 TestIfPlayerTouchesCustomElement(jx, jy);
13482 if (!player->active)
13483 RemovePlayer(player);
13488 void ScrollPlayer(struct PlayerInfo *player, int mode)
13490 int jx = player->jx, jy = player->jy;
13491 int last_jx = player->last_jx, last_jy = player->last_jy;
13492 int move_stepsize = TILEX / player->move_delay_value;
13494 #if USE_NEW_PLAYER_SPEED
13495 if (!player->active)
13498 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13501 if (!player->active || player->MovPos == 0)
13505 if (mode == SCROLL_INIT)
13507 player->actual_frame_counter = FrameCounter;
13508 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13510 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13511 Feld[last_jx][last_jy] == EL_EMPTY)
13513 int last_field_block_delay = 0; /* start with no blocking at all */
13514 int block_delay_adjustment = player->block_delay_adjustment;
13516 /* if player blocks last field, add delay for exactly one move */
13517 if (player->block_last_field)
13519 last_field_block_delay += player->move_delay_value;
13521 /* when blocking enabled, prevent moving up despite gravity */
13522 #if USE_PLAYER_GRAVITY
13523 if (player->gravity && player->MovDir == MV_UP)
13524 block_delay_adjustment = -1;
13526 if (game.gravity && player->MovDir == MV_UP)
13527 block_delay_adjustment = -1;
13531 /* add block delay adjustment (also possible when not blocking) */
13532 last_field_block_delay += block_delay_adjustment;
13534 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13535 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13538 #if USE_NEW_PLAYER_SPEED
13539 if (player->MovPos != 0) /* player has not yet reached destination */
13545 else if (!FrameReached(&player->actual_frame_counter, 1))
13548 #if USE_NEW_PLAYER_SPEED
13549 if (player->MovPos != 0)
13551 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13552 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13554 /* before DrawPlayer() to draw correct player graphic for this case */
13555 if (player->MovPos == 0)
13556 CheckGravityMovement(player);
13559 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13560 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13562 /* before DrawPlayer() to draw correct player graphic for this case */
13563 if (player->MovPos == 0)
13564 CheckGravityMovement(player);
13567 if (player->MovPos == 0) /* player reached destination field */
13569 if (player->move_delay_reset_counter > 0)
13571 player->move_delay_reset_counter--;
13573 if (player->move_delay_reset_counter == 0)
13575 /* continue with normal speed after quickly moving through gate */
13576 HALVE_PLAYER_SPEED(player);
13578 /* be able to make the next move without delay */
13579 player->move_delay = 0;
13583 player->last_jx = jx;
13584 player->last_jy = jy;
13586 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13587 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13588 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13589 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13590 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13591 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13593 DrawPlayer(player); /* needed here only to cleanup last field */
13594 RemovePlayer(player);
13596 if (local_player->friends_still_needed == 0 ||
13597 IS_SP_ELEMENT(Feld[jx][jy]))
13598 PlayerWins(player);
13601 /* this breaks one level: "machine", level 000 */
13603 int move_direction = player->MovDir;
13604 int enter_side = MV_DIR_OPPOSITE(move_direction);
13605 int leave_side = move_direction;
13606 int old_jx = last_jx;
13607 int old_jy = last_jy;
13608 int old_element = Feld[old_jx][old_jy];
13609 int new_element = Feld[jx][jy];
13611 if (IS_CUSTOM_ELEMENT(old_element))
13612 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13614 player->index_bit, leave_side);
13616 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13617 CE_PLAYER_LEAVES_X,
13618 player->index_bit, leave_side);
13620 if (IS_CUSTOM_ELEMENT(new_element))
13621 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13622 player->index_bit, enter_side);
13624 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13625 CE_PLAYER_ENTERS_X,
13626 player->index_bit, enter_side);
13628 #if USE_FIX_CE_ACTION_WITH_PLAYER
13629 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13630 CE_MOVE_OF_X, move_direction);
13632 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13633 CE_MOVE_OF_X, move_direction);
13637 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13639 TestIfPlayerTouchesBadThing(jx, jy);
13640 TestIfPlayerTouchesCustomElement(jx, jy);
13642 /* needed because pushed element has not yet reached its destination,
13643 so it would trigger a change event at its previous field location */
13644 if (!player->is_pushing)
13645 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13647 if (!player->active)
13648 RemovePlayer(player);
13651 if (!local_player->LevelSolved && level.use_step_counter)
13661 if (TimeLeft <= 10 && setup.time_limit)
13662 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13665 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13667 DisplayGameControlValues();
13669 DrawGameValue_Time(TimeLeft);
13672 if (!TimeLeft && setup.time_limit)
13673 for (i = 0; i < MAX_PLAYERS; i++)
13674 KillPlayer(&stored_player[i]);
13677 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13679 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13681 DisplayGameControlValues();
13684 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13685 DrawGameValue_Time(TimePlayed);
13689 if (tape.single_step && tape.recording && !tape.pausing &&
13690 !player->programmed_action)
13691 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13695 void ScrollScreen(struct PlayerInfo *player, int mode)
13697 static unsigned long screen_frame_counter = 0;
13699 if (mode == SCROLL_INIT)
13701 /* set scrolling step size according to actual player's moving speed */
13702 ScrollStepSize = TILEX / player->move_delay_value;
13704 screen_frame_counter = FrameCounter;
13705 ScreenMovDir = player->MovDir;
13706 ScreenMovPos = player->MovPos;
13707 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13710 else if (!FrameReached(&screen_frame_counter, 1))
13715 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13716 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13717 redraw_mask |= REDRAW_FIELD;
13720 ScreenMovDir = MV_NONE;
13723 void TestIfPlayerTouchesCustomElement(int x, int y)
13725 static int xy[4][2] =
13732 static int trigger_sides[4][2] =
13734 /* center side border side */
13735 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13736 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13737 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13738 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13740 static int touch_dir[4] =
13742 MV_LEFT | MV_RIGHT,
13747 int center_element = Feld[x][y]; /* should always be non-moving! */
13750 for (i = 0; i < NUM_DIRECTIONS; i++)
13752 int xx = x + xy[i][0];
13753 int yy = y + xy[i][1];
13754 int center_side = trigger_sides[i][0];
13755 int border_side = trigger_sides[i][1];
13756 int border_element;
13758 if (!IN_LEV_FIELD(xx, yy))
13761 if (IS_PLAYER(x, y)) /* player found at center element */
13763 struct PlayerInfo *player = PLAYERINFO(x, y);
13765 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13766 border_element = Feld[xx][yy]; /* may be moving! */
13767 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13768 border_element = Feld[xx][yy];
13769 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13770 border_element = MovingOrBlocked2Element(xx, yy);
13772 continue; /* center and border element do not touch */
13774 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13775 player->index_bit, border_side);
13776 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13777 CE_PLAYER_TOUCHES_X,
13778 player->index_bit, border_side);
13780 #if USE_FIX_CE_ACTION_WITH_PLAYER
13782 /* use player element that is initially defined in the level playfield,
13783 not the player element that corresponds to the runtime player number
13784 (example: a level that contains EL_PLAYER_3 as the only player would
13785 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13786 int player_element = PLAYERINFO(x, y)->initial_element;
13788 CheckElementChangeBySide(xx, yy, border_element, player_element,
13789 CE_TOUCHING_X, border_side);
13793 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13795 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13797 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13799 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13800 continue; /* center and border element do not touch */
13803 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13804 player->index_bit, center_side);
13805 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13806 CE_PLAYER_TOUCHES_X,
13807 player->index_bit, center_side);
13809 #if USE_FIX_CE_ACTION_WITH_PLAYER
13811 /* use player element that is initially defined in the level playfield,
13812 not the player element that corresponds to the runtime player number
13813 (example: a level that contains EL_PLAYER_3 as the only player would
13814 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13815 int player_element = PLAYERINFO(xx, yy)->initial_element;
13817 CheckElementChangeBySide(x, y, center_element, player_element,
13818 CE_TOUCHING_X, center_side);
13827 #if USE_ELEMENT_TOUCHING_BUGFIX
13829 void TestIfElementTouchesCustomElement(int x, int y)
13831 static int xy[4][2] =
13838 static int trigger_sides[4][2] =
13840 /* center side border side */
13841 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13842 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13843 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13844 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13846 static int touch_dir[4] =
13848 MV_LEFT | MV_RIGHT,
13853 boolean change_center_element = FALSE;
13854 int center_element = Feld[x][y]; /* should always be non-moving! */
13855 int border_element_old[NUM_DIRECTIONS];
13858 for (i = 0; i < NUM_DIRECTIONS; i++)
13860 int xx = x + xy[i][0];
13861 int yy = y + xy[i][1];
13862 int border_element;
13864 border_element_old[i] = -1;
13866 if (!IN_LEV_FIELD(xx, yy))
13869 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13870 border_element = Feld[xx][yy]; /* may be moving! */
13871 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13872 border_element = Feld[xx][yy];
13873 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13874 border_element = MovingOrBlocked2Element(xx, yy);
13876 continue; /* center and border element do not touch */
13878 border_element_old[i] = border_element;
13881 for (i = 0; i < NUM_DIRECTIONS; i++)
13883 int xx = x + xy[i][0];
13884 int yy = y + xy[i][1];
13885 int center_side = trigger_sides[i][0];
13886 int border_element = border_element_old[i];
13888 if (border_element == -1)
13891 /* check for change of border element */
13892 CheckElementChangeBySide(xx, yy, border_element, center_element,
13893 CE_TOUCHING_X, center_side);
13895 /* (center element cannot be player, so we dont have to check this here) */
13898 for (i = 0; i < NUM_DIRECTIONS; i++)
13900 int xx = x + xy[i][0];
13901 int yy = y + xy[i][1];
13902 int border_side = trigger_sides[i][1];
13903 int border_element = border_element_old[i];
13905 if (border_element == -1)
13908 /* check for change of center element (but change it only once) */
13909 if (!change_center_element)
13910 change_center_element =
13911 CheckElementChangeBySide(x, y, center_element, border_element,
13912 CE_TOUCHING_X, border_side);
13914 #if USE_FIX_CE_ACTION_WITH_PLAYER
13915 if (IS_PLAYER(xx, yy))
13917 /* use player element that is initially defined in the level playfield,
13918 not the player element that corresponds to the runtime player number
13919 (example: a level that contains EL_PLAYER_3 as the only player would
13920 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13921 int player_element = PLAYERINFO(xx, yy)->initial_element;
13923 CheckElementChangeBySide(x, y, center_element, player_element,
13924 CE_TOUCHING_X, border_side);
13932 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13934 static int xy[4][2] =
13941 static int trigger_sides[4][2] =
13943 /* center side border side */
13944 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13945 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13946 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13947 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13949 static int touch_dir[4] =
13951 MV_LEFT | MV_RIGHT,
13956 boolean change_center_element = FALSE;
13957 int center_element = Feld[x][y]; /* should always be non-moving! */
13960 for (i = 0; i < NUM_DIRECTIONS; i++)
13962 int xx = x + xy[i][0];
13963 int yy = y + xy[i][1];
13964 int center_side = trigger_sides[i][0];
13965 int border_side = trigger_sides[i][1];
13966 int border_element;
13968 if (!IN_LEV_FIELD(xx, yy))
13971 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13972 border_element = Feld[xx][yy]; /* may be moving! */
13973 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13974 border_element = Feld[xx][yy];
13975 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13976 border_element = MovingOrBlocked2Element(xx, yy);
13978 continue; /* center and border element do not touch */
13980 /* check for change of center element (but change it only once) */
13981 if (!change_center_element)
13982 change_center_element =
13983 CheckElementChangeBySide(x, y, center_element, border_element,
13984 CE_TOUCHING_X, border_side);
13986 /* check for change of border element */
13987 CheckElementChangeBySide(xx, yy, border_element, center_element,
13988 CE_TOUCHING_X, center_side);
13994 void TestIfElementHitsCustomElement(int x, int y, int direction)
13996 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13997 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13998 int hitx = x + dx, hity = y + dy;
13999 int hitting_element = Feld[x][y];
14000 int touched_element;
14002 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14005 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14006 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14008 if (IN_LEV_FIELD(hitx, hity))
14010 int opposite_direction = MV_DIR_OPPOSITE(direction);
14011 int hitting_side = direction;
14012 int touched_side = opposite_direction;
14013 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14014 MovDir[hitx][hity] != direction ||
14015 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14021 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14022 CE_HITTING_X, touched_side);
14024 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14025 CE_HIT_BY_X, hitting_side);
14027 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14028 CE_HIT_BY_SOMETHING, opposite_direction);
14030 #if USE_FIX_CE_ACTION_WITH_PLAYER
14031 if (IS_PLAYER(hitx, hity))
14033 /* use player element that is initially defined in the level playfield,
14034 not the player element that corresponds to the runtime player number
14035 (example: a level that contains EL_PLAYER_3 as the only player would
14036 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14037 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14039 CheckElementChangeBySide(x, y, hitting_element, player_element,
14040 CE_HITTING_X, touched_side);
14046 /* "hitting something" is also true when hitting the playfield border */
14047 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14048 CE_HITTING_SOMETHING, direction);
14052 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14054 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14055 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14056 int hitx = x + dx, hity = y + dy;
14057 int hitting_element = Feld[x][y];
14058 int touched_element;
14060 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14061 !IS_FREE(hitx, hity) &&
14062 (!IS_MOVING(hitx, hity) ||
14063 MovDir[hitx][hity] != direction ||
14064 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14067 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14071 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14075 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14076 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14078 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14079 EP_CAN_SMASH_EVERYTHING, direction);
14081 if (IN_LEV_FIELD(hitx, hity))
14083 int opposite_direction = MV_DIR_OPPOSITE(direction);
14084 int hitting_side = direction;
14085 int touched_side = opposite_direction;
14087 int touched_element = MovingOrBlocked2Element(hitx, hity);
14090 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14091 MovDir[hitx][hity] != direction ||
14092 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14101 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14102 CE_SMASHED_BY_SOMETHING, opposite_direction);
14104 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14105 CE_OTHER_IS_SMASHING, touched_side);
14107 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14108 CE_OTHER_GETS_SMASHED, hitting_side);
14114 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14116 int i, kill_x = -1, kill_y = -1;
14118 int bad_element = -1;
14119 static int test_xy[4][2] =
14126 static int test_dir[4] =
14134 for (i = 0; i < NUM_DIRECTIONS; i++)
14136 int test_x, test_y, test_move_dir, test_element;
14138 test_x = good_x + test_xy[i][0];
14139 test_y = good_y + test_xy[i][1];
14141 if (!IN_LEV_FIELD(test_x, test_y))
14145 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14147 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14149 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14150 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14152 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14153 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14157 bad_element = test_element;
14163 if (kill_x != -1 || kill_y != -1)
14165 if (IS_PLAYER(good_x, good_y))
14167 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14169 if (player->shield_deadly_time_left > 0 &&
14170 !IS_INDESTRUCTIBLE(bad_element))
14171 Bang(kill_x, kill_y);
14172 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14173 KillPlayer(player);
14176 Bang(good_x, good_y);
14180 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14182 int i, kill_x = -1, kill_y = -1;
14183 int bad_element = Feld[bad_x][bad_y];
14184 static int test_xy[4][2] =
14191 static int touch_dir[4] =
14193 MV_LEFT | MV_RIGHT,
14198 static int test_dir[4] =
14206 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14209 for (i = 0; i < NUM_DIRECTIONS; i++)
14211 int test_x, test_y, test_move_dir, test_element;
14213 test_x = bad_x + test_xy[i][0];
14214 test_y = bad_y + test_xy[i][1];
14216 if (!IN_LEV_FIELD(test_x, test_y))
14220 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14222 test_element = Feld[test_x][test_y];
14224 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14225 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14227 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14228 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14230 /* good thing is player or penguin that does not move away */
14231 if (IS_PLAYER(test_x, test_y))
14233 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14235 if (bad_element == EL_ROBOT && player->is_moving)
14236 continue; /* robot does not kill player if he is moving */
14238 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14240 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14241 continue; /* center and border element do not touch */
14249 else if (test_element == EL_PENGUIN)
14259 if (kill_x != -1 || kill_y != -1)
14261 if (IS_PLAYER(kill_x, kill_y))
14263 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14265 if (player->shield_deadly_time_left > 0 &&
14266 !IS_INDESTRUCTIBLE(bad_element))
14267 Bang(bad_x, bad_y);
14268 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14269 KillPlayer(player);
14272 Bang(kill_x, kill_y);
14276 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14278 int bad_element = Feld[bad_x][bad_y];
14279 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14280 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14281 int test_x = bad_x + dx, test_y = bad_y + dy;
14282 int test_move_dir, test_element;
14283 int kill_x = -1, kill_y = -1;
14285 if (!IN_LEV_FIELD(test_x, test_y))
14289 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14291 test_element = Feld[test_x][test_y];
14293 if (test_move_dir != bad_move_dir)
14295 /* good thing can be player or penguin that does not move away */
14296 if (IS_PLAYER(test_x, test_y))
14298 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14300 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14301 player as being hit when he is moving towards the bad thing, because
14302 the "get hit by" condition would be lost after the player stops) */
14303 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14304 return; /* player moves away from bad thing */
14309 else if (test_element == EL_PENGUIN)
14316 if (kill_x != -1 || kill_y != -1)
14318 if (IS_PLAYER(kill_x, kill_y))
14320 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14322 if (player->shield_deadly_time_left > 0 &&
14323 !IS_INDESTRUCTIBLE(bad_element))
14324 Bang(bad_x, bad_y);
14325 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14326 KillPlayer(player);
14329 Bang(kill_x, kill_y);
14333 void TestIfPlayerTouchesBadThing(int x, int y)
14335 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14338 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14340 TestIfGoodThingHitsBadThing(x, y, move_dir);
14343 void TestIfBadThingTouchesPlayer(int x, int y)
14345 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14348 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14350 TestIfBadThingHitsGoodThing(x, y, move_dir);
14353 void TestIfFriendTouchesBadThing(int x, int y)
14355 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14358 void TestIfBadThingTouchesFriend(int x, int y)
14360 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14363 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14365 int i, kill_x = bad_x, kill_y = bad_y;
14366 static int xy[4][2] =
14374 for (i = 0; i < NUM_DIRECTIONS; i++)
14378 x = bad_x + xy[i][0];
14379 y = bad_y + xy[i][1];
14380 if (!IN_LEV_FIELD(x, y))
14383 element = Feld[x][y];
14384 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14385 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14393 if (kill_x != bad_x || kill_y != bad_y)
14394 Bang(bad_x, bad_y);
14397 void KillPlayer(struct PlayerInfo *player)
14399 int jx = player->jx, jy = player->jy;
14401 if (!player->active)
14405 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14406 player->killed, player->active, player->reanimated);
14409 /* the following code was introduced to prevent an infinite loop when calling
14411 -> CheckTriggeredElementChangeExt()
14412 -> ExecuteCustomElementAction()
14414 -> (infinitely repeating the above sequence of function calls)
14415 which occurs when killing the player while having a CE with the setting
14416 "kill player X when explosion of <player X>"; the solution using a new
14417 field "player->killed" was chosen for backwards compatibility, although
14418 clever use of the fields "player->active" etc. would probably also work */
14420 if (player->killed)
14424 player->killed = TRUE;
14426 /* remove accessible field at the player's position */
14427 Feld[jx][jy] = EL_EMPTY;
14429 /* deactivate shield (else Bang()/Explode() would not work right) */
14430 player->shield_normal_time_left = 0;
14431 player->shield_deadly_time_left = 0;
14434 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14435 player->killed, player->active, player->reanimated);
14441 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14442 player->killed, player->active, player->reanimated);
14445 #if USE_PLAYER_REANIMATION
14447 if (player->reanimated) /* killed player may have been reanimated */
14448 player->killed = player->reanimated = FALSE;
14450 BuryPlayer(player);
14452 if (player->killed) /* player may have been reanimated */
14453 BuryPlayer(player);
14456 BuryPlayer(player);
14460 static void KillPlayerUnlessEnemyProtected(int x, int y)
14462 if (!PLAYER_ENEMY_PROTECTED(x, y))
14463 KillPlayer(PLAYERINFO(x, y));
14466 static void KillPlayerUnlessExplosionProtected(int x, int y)
14468 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14469 KillPlayer(PLAYERINFO(x, y));
14472 void BuryPlayer(struct PlayerInfo *player)
14474 int jx = player->jx, jy = player->jy;
14476 if (!player->active)
14479 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14480 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14482 player->GameOver = TRUE;
14483 RemovePlayer(player);
14486 void RemovePlayer(struct PlayerInfo *player)
14488 int jx = player->jx, jy = player->jy;
14489 int i, found = FALSE;
14491 player->present = FALSE;
14492 player->active = FALSE;
14494 if (!ExplodeField[jx][jy])
14495 StorePlayer[jx][jy] = 0;
14497 if (player->is_moving)
14498 TEST_DrawLevelField(player->last_jx, player->last_jy);
14500 for (i = 0; i < MAX_PLAYERS; i++)
14501 if (stored_player[i].active)
14505 AllPlayersGone = TRUE;
14511 #if USE_NEW_SNAP_DELAY
14512 static void setFieldForSnapping(int x, int y, int element, int direction)
14514 struct ElementInfo *ei = &element_info[element];
14515 int direction_bit = MV_DIR_TO_BIT(direction);
14516 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14517 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14518 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14520 Feld[x][y] = EL_ELEMENT_SNAPPING;
14521 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14523 ResetGfxAnimation(x, y);
14525 GfxElement[x][y] = element;
14526 GfxAction[x][y] = action;
14527 GfxDir[x][y] = direction;
14528 GfxFrame[x][y] = -1;
14533 =============================================================================
14534 checkDiagonalPushing()
14535 -----------------------------------------------------------------------------
14536 check if diagonal input device direction results in pushing of object
14537 (by checking if the alternative direction is walkable, diggable, ...)
14538 =============================================================================
14541 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14542 int x, int y, int real_dx, int real_dy)
14544 int jx, jy, dx, dy, xx, yy;
14546 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14549 /* diagonal direction: check alternative direction */
14554 xx = jx + (dx == 0 ? real_dx : 0);
14555 yy = jy + (dy == 0 ? real_dy : 0);
14557 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14561 =============================================================================
14563 -----------------------------------------------------------------------------
14564 x, y: field next to player (non-diagonal) to try to dig to
14565 real_dx, real_dy: direction as read from input device (can be diagonal)
14566 =============================================================================
14569 static int DigField(struct PlayerInfo *player,
14570 int oldx, int oldy, int x, int y,
14571 int real_dx, int real_dy, int mode)
14573 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14574 boolean player_was_pushing = player->is_pushing;
14575 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14576 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14577 int jx = oldx, jy = oldy;
14578 int dx = x - jx, dy = y - jy;
14579 int nextx = x + dx, nexty = y + dy;
14580 int move_direction = (dx == -1 ? MV_LEFT :
14581 dx == +1 ? MV_RIGHT :
14583 dy == +1 ? MV_DOWN : MV_NONE);
14584 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14585 int dig_side = MV_DIR_OPPOSITE(move_direction);
14586 int old_element = Feld[jx][jy];
14587 #if USE_FIXED_DONT_RUN_INTO
14588 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14594 if (is_player) /* function can also be called by EL_PENGUIN */
14596 if (player->MovPos == 0)
14598 player->is_digging = FALSE;
14599 player->is_collecting = FALSE;
14602 if (player->MovPos == 0) /* last pushing move finished */
14603 player->is_pushing = FALSE;
14605 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14607 player->is_switching = FALSE;
14608 player->push_delay = -1;
14610 return MP_NO_ACTION;
14614 #if !USE_FIXED_DONT_RUN_INTO
14615 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14616 return MP_NO_ACTION;
14619 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14620 old_element = Back[jx][jy];
14622 /* in case of element dropped at player position, check background */
14623 else if (Back[jx][jy] != EL_EMPTY &&
14624 game.engine_version >= VERSION_IDENT(2,2,0,0))
14625 old_element = Back[jx][jy];
14627 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14628 return MP_NO_ACTION; /* field has no opening in this direction */
14630 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14631 return MP_NO_ACTION; /* field has no opening in this direction */
14633 #if USE_FIXED_DONT_RUN_INTO
14634 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14638 Feld[jx][jy] = player->artwork_element;
14639 InitMovingField(jx, jy, MV_DOWN);
14640 Store[jx][jy] = EL_ACID;
14641 ContinueMoving(jx, jy);
14642 BuryPlayer(player);
14644 return MP_DONT_RUN_INTO;
14648 #if USE_FIXED_DONT_RUN_INTO
14649 if (player_can_move && DONT_RUN_INTO(element))
14651 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14653 return MP_DONT_RUN_INTO;
14657 #if USE_FIXED_DONT_RUN_INTO
14658 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14659 return MP_NO_ACTION;
14662 #if !USE_FIXED_DONT_RUN_INTO
14663 element = Feld[x][y];
14666 collect_count = element_info[element].collect_count_initial;
14668 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14669 return MP_NO_ACTION;
14671 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14672 player_can_move = player_can_move_or_snap;
14674 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14675 game.engine_version >= VERSION_IDENT(2,2,0,0))
14677 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14678 player->index_bit, dig_side);
14679 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14680 player->index_bit, dig_side);
14682 if (element == EL_DC_LANDMINE)
14685 if (Feld[x][y] != element) /* field changed by snapping */
14688 return MP_NO_ACTION;
14691 #if USE_PLAYER_GRAVITY
14692 if (player->gravity && is_player && !player->is_auto_moving &&
14693 canFallDown(player) && move_direction != MV_DOWN &&
14694 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14695 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14697 if (game.gravity && is_player && !player->is_auto_moving &&
14698 canFallDown(player) && move_direction != MV_DOWN &&
14699 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14700 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14703 if (player_can_move &&
14704 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14706 int sound_element = SND_ELEMENT(element);
14707 int sound_action = ACTION_WALKING;
14709 if (IS_RND_GATE(element))
14711 if (!player->key[RND_GATE_NR(element)])
14712 return MP_NO_ACTION;
14714 else if (IS_RND_GATE_GRAY(element))
14716 if (!player->key[RND_GATE_GRAY_NR(element)])
14717 return MP_NO_ACTION;
14719 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14721 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14722 return MP_NO_ACTION;
14724 else if (element == EL_EXIT_OPEN ||
14725 element == EL_EM_EXIT_OPEN ||
14726 element == EL_STEEL_EXIT_OPEN ||
14727 element == EL_EM_STEEL_EXIT_OPEN ||
14728 element == EL_SP_EXIT_OPEN ||
14729 element == EL_SP_EXIT_OPENING)
14731 sound_action = ACTION_PASSING; /* player is passing exit */
14733 else if (element == EL_EMPTY)
14735 sound_action = ACTION_MOVING; /* nothing to walk on */
14738 /* play sound from background or player, whatever is available */
14739 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14740 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14742 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14744 else if (player_can_move &&
14745 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14747 if (!ACCESS_FROM(element, opposite_direction))
14748 return MP_NO_ACTION; /* field not accessible from this direction */
14750 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14751 return MP_NO_ACTION;
14753 if (IS_EM_GATE(element))
14755 if (!player->key[EM_GATE_NR(element)])
14756 return MP_NO_ACTION;
14758 else if (IS_EM_GATE_GRAY(element))
14760 if (!player->key[EM_GATE_GRAY_NR(element)])
14761 return MP_NO_ACTION;
14763 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14765 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14766 return MP_NO_ACTION;
14768 else if (IS_EMC_GATE(element))
14770 if (!player->key[EMC_GATE_NR(element)])
14771 return MP_NO_ACTION;
14773 else if (IS_EMC_GATE_GRAY(element))
14775 if (!player->key[EMC_GATE_GRAY_NR(element)])
14776 return MP_NO_ACTION;
14778 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14780 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14781 return MP_NO_ACTION;
14783 else if (element == EL_DC_GATE_WHITE ||
14784 element == EL_DC_GATE_WHITE_GRAY ||
14785 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14787 if (player->num_white_keys == 0)
14788 return MP_NO_ACTION;
14790 player->num_white_keys--;
14792 else if (IS_SP_PORT(element))
14794 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14795 element == EL_SP_GRAVITY_PORT_RIGHT ||
14796 element == EL_SP_GRAVITY_PORT_UP ||
14797 element == EL_SP_GRAVITY_PORT_DOWN)
14798 #if USE_PLAYER_GRAVITY
14799 player->gravity = !player->gravity;
14801 game.gravity = !game.gravity;
14803 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14804 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14805 element == EL_SP_GRAVITY_ON_PORT_UP ||
14806 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14807 #if USE_PLAYER_GRAVITY
14808 player->gravity = TRUE;
14810 game.gravity = TRUE;
14812 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14813 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14814 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14815 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14816 #if USE_PLAYER_GRAVITY
14817 player->gravity = FALSE;
14819 game.gravity = FALSE;
14823 /* automatically move to the next field with double speed */
14824 player->programmed_action = move_direction;
14826 if (player->move_delay_reset_counter == 0)
14828 player->move_delay_reset_counter = 2; /* two double speed steps */
14830 DOUBLE_PLAYER_SPEED(player);
14833 PlayLevelSoundAction(x, y, ACTION_PASSING);
14835 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14839 if (mode != DF_SNAP)
14841 GfxElement[x][y] = GFX_ELEMENT(element);
14842 player->is_digging = TRUE;
14845 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14847 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14848 player->index_bit, dig_side);
14850 if (mode == DF_SNAP)
14852 #if USE_NEW_SNAP_DELAY
14853 if (level.block_snap_field)
14854 setFieldForSnapping(x, y, element, move_direction);
14856 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14858 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14861 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14862 player->index_bit, dig_side);
14865 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14869 if (is_player && mode != DF_SNAP)
14871 GfxElement[x][y] = element;
14872 player->is_collecting = TRUE;
14875 if (element == EL_SPEED_PILL)
14877 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14879 else if (element == EL_EXTRA_TIME && level.time > 0)
14881 TimeLeft += level.extra_time;
14884 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14886 DisplayGameControlValues();
14888 DrawGameValue_Time(TimeLeft);
14891 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14893 player->shield_normal_time_left += level.shield_normal_time;
14894 if (element == EL_SHIELD_DEADLY)
14895 player->shield_deadly_time_left += level.shield_deadly_time;
14897 else if (element == EL_DYNAMITE ||
14898 element == EL_EM_DYNAMITE ||
14899 element == EL_SP_DISK_RED)
14901 if (player->inventory_size < MAX_INVENTORY_SIZE)
14902 player->inventory_element[player->inventory_size++] = element;
14904 DrawGameDoorValues();
14906 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14908 player->dynabomb_count++;
14909 player->dynabombs_left++;
14911 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14913 player->dynabomb_size++;
14915 else if (element == EL_DYNABOMB_INCREASE_POWER)
14917 player->dynabomb_xl = TRUE;
14919 else if (IS_KEY(element))
14921 player->key[KEY_NR(element)] = TRUE;
14923 DrawGameDoorValues();
14925 else if (element == EL_DC_KEY_WHITE)
14927 player->num_white_keys++;
14929 /* display white keys? */
14930 /* DrawGameDoorValues(); */
14932 else if (IS_ENVELOPE(element))
14934 player->show_envelope = element;
14936 else if (element == EL_EMC_LENSES)
14938 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14940 RedrawAllInvisibleElementsForLenses();
14942 else if (element == EL_EMC_MAGNIFIER)
14944 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14946 RedrawAllInvisibleElementsForMagnifier();
14948 else if (IS_DROPPABLE(element) ||
14949 IS_THROWABLE(element)) /* can be collected and dropped */
14953 if (collect_count == 0)
14954 player->inventory_infinite_element = element;
14956 for (i = 0; i < collect_count; i++)
14957 if (player->inventory_size < MAX_INVENTORY_SIZE)
14958 player->inventory_element[player->inventory_size++] = element;
14960 DrawGameDoorValues();
14962 else if (collect_count > 0)
14964 local_player->gems_still_needed -= collect_count;
14965 if (local_player->gems_still_needed < 0)
14966 local_player->gems_still_needed = 0;
14969 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14971 DisplayGameControlValues();
14973 DrawGameValue_Emeralds(local_player->gems_still_needed);
14977 RaiseScoreElement(element);
14978 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14981 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14982 player->index_bit, dig_side);
14984 if (mode == DF_SNAP)
14986 #if USE_NEW_SNAP_DELAY
14987 if (level.block_snap_field)
14988 setFieldForSnapping(x, y, element, move_direction);
14990 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14992 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14995 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14996 player->index_bit, dig_side);
14999 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15001 if (mode == DF_SNAP && element != EL_BD_ROCK)
15002 return MP_NO_ACTION;
15004 if (CAN_FALL(element) && dy)
15005 return MP_NO_ACTION;
15007 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15008 !(element == EL_SPRING && level.use_spring_bug))
15009 return MP_NO_ACTION;
15011 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15012 ((move_direction & MV_VERTICAL &&
15013 ((element_info[element].move_pattern & MV_LEFT &&
15014 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15015 (element_info[element].move_pattern & MV_RIGHT &&
15016 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15017 (move_direction & MV_HORIZONTAL &&
15018 ((element_info[element].move_pattern & MV_UP &&
15019 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15020 (element_info[element].move_pattern & MV_DOWN &&
15021 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15022 return MP_NO_ACTION;
15024 /* do not push elements already moving away faster than player */
15025 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15026 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15027 return MP_NO_ACTION;
15029 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15031 if (player->push_delay_value == -1 || !player_was_pushing)
15032 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15034 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15036 if (player->push_delay_value == -1)
15037 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15039 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15041 if (!player->is_pushing)
15042 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15045 player->is_pushing = TRUE;
15046 player->is_active = TRUE;
15048 if (!(IN_LEV_FIELD(nextx, nexty) &&
15049 (IS_FREE(nextx, nexty) ||
15050 (IS_SB_ELEMENT(element) &&
15051 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15052 (IS_CUSTOM_ELEMENT(element) &&
15053 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15054 return MP_NO_ACTION;
15056 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15057 return MP_NO_ACTION;
15059 if (player->push_delay == -1) /* new pushing; restart delay */
15060 player->push_delay = 0;
15062 if (player->push_delay < player->push_delay_value &&
15063 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15064 element != EL_SPRING && element != EL_BALLOON)
15066 /* make sure that there is no move delay before next try to push */
15067 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15068 player->move_delay = 0;
15070 return MP_NO_ACTION;
15073 if (IS_CUSTOM_ELEMENT(element) &&
15074 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15076 if (!DigFieldByCE(nextx, nexty, element))
15077 return MP_NO_ACTION;
15080 if (IS_SB_ELEMENT(element))
15082 if (element == EL_SOKOBAN_FIELD_FULL)
15084 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15085 local_player->sokobanfields_still_needed++;
15088 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15090 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15091 local_player->sokobanfields_still_needed--;
15094 Feld[x][y] = EL_SOKOBAN_OBJECT;
15096 if (Back[x][y] == Back[nextx][nexty])
15097 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15098 else if (Back[x][y] != 0)
15099 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15102 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15105 if (local_player->sokobanfields_still_needed == 0 &&
15106 game.emulation == EMU_SOKOBAN)
15108 PlayerWins(player);
15110 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15114 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15116 InitMovingField(x, y, move_direction);
15117 GfxAction[x][y] = ACTION_PUSHING;
15119 if (mode == DF_SNAP)
15120 ContinueMoving(x, y);
15122 MovPos[x][y] = (dx != 0 ? dx : dy);
15124 Pushed[x][y] = TRUE;
15125 Pushed[nextx][nexty] = TRUE;
15127 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15128 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15130 player->push_delay_value = -1; /* get new value later */
15132 /* check for element change _after_ element has been pushed */
15133 if (game.use_change_when_pushing_bug)
15135 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15136 player->index_bit, dig_side);
15137 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15138 player->index_bit, dig_side);
15141 else if (IS_SWITCHABLE(element))
15143 if (PLAYER_SWITCHING(player, x, y))
15145 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15146 player->index_bit, dig_side);
15151 player->is_switching = TRUE;
15152 player->switch_x = x;
15153 player->switch_y = y;
15155 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15157 if (element == EL_ROBOT_WHEEL)
15159 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15163 game.robot_wheel_active = TRUE;
15165 TEST_DrawLevelField(x, y);
15167 else if (element == EL_SP_TERMINAL)
15171 SCAN_PLAYFIELD(xx, yy)
15173 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15175 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15176 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15179 else if (IS_BELT_SWITCH(element))
15181 ToggleBeltSwitch(x, y);
15183 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15184 element == EL_SWITCHGATE_SWITCH_DOWN ||
15185 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15186 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15188 ToggleSwitchgateSwitch(x, y);
15190 else if (element == EL_LIGHT_SWITCH ||
15191 element == EL_LIGHT_SWITCH_ACTIVE)
15193 ToggleLightSwitch(x, y);
15195 else if (element == EL_TIMEGATE_SWITCH ||
15196 element == EL_DC_TIMEGATE_SWITCH)
15198 ActivateTimegateSwitch(x, y);
15200 else if (element == EL_BALLOON_SWITCH_LEFT ||
15201 element == EL_BALLOON_SWITCH_RIGHT ||
15202 element == EL_BALLOON_SWITCH_UP ||
15203 element == EL_BALLOON_SWITCH_DOWN ||
15204 element == EL_BALLOON_SWITCH_NONE ||
15205 element == EL_BALLOON_SWITCH_ANY)
15207 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15208 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15209 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15210 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15211 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15214 else if (element == EL_LAMP)
15216 Feld[x][y] = EL_LAMP_ACTIVE;
15217 local_player->lights_still_needed--;
15219 ResetGfxAnimation(x, y);
15220 TEST_DrawLevelField(x, y);
15222 else if (element == EL_TIME_ORB_FULL)
15224 Feld[x][y] = EL_TIME_ORB_EMPTY;
15226 if (level.time > 0 || level.use_time_orb_bug)
15228 TimeLeft += level.time_orb_time;
15231 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15233 DisplayGameControlValues();
15235 DrawGameValue_Time(TimeLeft);
15239 ResetGfxAnimation(x, y);
15240 TEST_DrawLevelField(x, y);
15242 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15243 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15247 game.ball_state = !game.ball_state;
15249 SCAN_PLAYFIELD(xx, yy)
15251 int e = Feld[xx][yy];
15253 if (game.ball_state)
15255 if (e == EL_EMC_MAGIC_BALL)
15256 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15257 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15258 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15262 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15263 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15264 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15265 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15270 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15271 player->index_bit, dig_side);
15273 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15274 player->index_bit, dig_side);
15276 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15277 player->index_bit, dig_side);
15283 if (!PLAYER_SWITCHING(player, x, y))
15285 player->is_switching = TRUE;
15286 player->switch_x = x;
15287 player->switch_y = y;
15289 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15290 player->index_bit, dig_side);
15291 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15292 player->index_bit, dig_side);
15294 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15295 player->index_bit, dig_side);
15296 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15297 player->index_bit, dig_side);
15300 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15301 player->index_bit, dig_side);
15302 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15303 player->index_bit, dig_side);
15305 return MP_NO_ACTION;
15308 player->push_delay = -1;
15310 if (is_player) /* function can also be called by EL_PENGUIN */
15312 if (Feld[x][y] != element) /* really digged/collected something */
15314 player->is_collecting = !player->is_digging;
15315 player->is_active = TRUE;
15322 static boolean DigFieldByCE(int x, int y, int digging_element)
15324 int element = Feld[x][y];
15326 if (!IS_FREE(x, y))
15328 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15329 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15332 /* no element can dig solid indestructible elements */
15333 if (IS_INDESTRUCTIBLE(element) &&
15334 !IS_DIGGABLE(element) &&
15335 !IS_COLLECTIBLE(element))
15338 if (AmoebaNr[x][y] &&
15339 (element == EL_AMOEBA_FULL ||
15340 element == EL_BD_AMOEBA ||
15341 element == EL_AMOEBA_GROWING))
15343 AmoebaCnt[AmoebaNr[x][y]]--;
15344 AmoebaCnt2[AmoebaNr[x][y]]--;
15347 if (IS_MOVING(x, y))
15348 RemoveMovingField(x, y);
15352 TEST_DrawLevelField(x, y);
15355 /* if digged element was about to explode, prevent the explosion */
15356 ExplodeField[x][y] = EX_TYPE_NONE;
15358 PlayLevelSoundAction(x, y, action);
15361 Store[x][y] = EL_EMPTY;
15364 /* this makes it possible to leave the removed element again */
15365 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15366 Store[x][y] = element;
15368 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15370 int move_leave_element = element_info[digging_element].move_leave_element;
15372 /* this makes it possible to leave the removed element again */
15373 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15374 element : move_leave_element);
15381 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15383 int jx = player->jx, jy = player->jy;
15384 int x = jx + dx, y = jy + dy;
15385 int snap_direction = (dx == -1 ? MV_LEFT :
15386 dx == +1 ? MV_RIGHT :
15388 dy == +1 ? MV_DOWN : MV_NONE);
15389 boolean can_continue_snapping = (level.continuous_snapping &&
15390 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15392 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15395 if (!player->active || !IN_LEV_FIELD(x, y))
15403 if (player->MovPos == 0)
15404 player->is_pushing = FALSE;
15406 player->is_snapping = FALSE;
15408 if (player->MovPos == 0)
15410 player->is_moving = FALSE;
15411 player->is_digging = FALSE;
15412 player->is_collecting = FALSE;
15418 #if USE_NEW_CONTINUOUS_SNAPPING
15419 /* prevent snapping with already pressed snap key when not allowed */
15420 if (player->is_snapping && !can_continue_snapping)
15423 if (player->is_snapping)
15427 player->MovDir = snap_direction;
15429 if (player->MovPos == 0)
15431 player->is_moving = FALSE;
15432 player->is_digging = FALSE;
15433 player->is_collecting = FALSE;
15436 player->is_dropping = FALSE;
15437 player->is_dropping_pressed = FALSE;
15438 player->drop_pressed_delay = 0;
15440 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15443 player->is_snapping = TRUE;
15444 player->is_active = TRUE;
15446 if (player->MovPos == 0)
15448 player->is_moving = FALSE;
15449 player->is_digging = FALSE;
15450 player->is_collecting = FALSE;
15453 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15454 TEST_DrawLevelField(player->last_jx, player->last_jy);
15456 TEST_DrawLevelField(x, y);
15461 static boolean DropElement(struct PlayerInfo *player)
15463 int old_element, new_element;
15464 int dropx = player->jx, dropy = player->jy;
15465 int drop_direction = player->MovDir;
15466 int drop_side = drop_direction;
15468 int drop_element = get_next_dropped_element(player);
15470 int drop_element = (player->inventory_size > 0 ?
15471 player->inventory_element[player->inventory_size - 1] :
15472 player->inventory_infinite_element != EL_UNDEFINED ?
15473 player->inventory_infinite_element :
15474 player->dynabombs_left > 0 ?
15475 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15479 player->is_dropping_pressed = TRUE;
15481 /* do not drop an element on top of another element; when holding drop key
15482 pressed without moving, dropped element must move away before the next
15483 element can be dropped (this is especially important if the next element
15484 is dynamite, which can be placed on background for historical reasons) */
15485 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15488 if (IS_THROWABLE(drop_element))
15490 dropx += GET_DX_FROM_DIR(drop_direction);
15491 dropy += GET_DY_FROM_DIR(drop_direction);
15493 if (!IN_LEV_FIELD(dropx, dropy))
15497 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15498 new_element = drop_element; /* default: no change when dropping */
15500 /* check if player is active, not moving and ready to drop */
15501 if (!player->active || player->MovPos || player->drop_delay > 0)
15504 /* check if player has anything that can be dropped */
15505 if (new_element == EL_UNDEFINED)
15508 /* check if drop key was pressed long enough for EM style dynamite */
15509 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15512 /* check if anything can be dropped at the current position */
15513 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15516 /* collected custom elements can only be dropped on empty fields */
15517 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15520 if (old_element != EL_EMPTY)
15521 Back[dropx][dropy] = old_element; /* store old element on this field */
15523 ResetGfxAnimation(dropx, dropy);
15524 ResetRandomAnimationValue(dropx, dropy);
15526 if (player->inventory_size > 0 ||
15527 player->inventory_infinite_element != EL_UNDEFINED)
15529 if (player->inventory_size > 0)
15531 player->inventory_size--;
15533 DrawGameDoorValues();
15535 if (new_element == EL_DYNAMITE)
15536 new_element = EL_DYNAMITE_ACTIVE;
15537 else if (new_element == EL_EM_DYNAMITE)
15538 new_element = EL_EM_DYNAMITE_ACTIVE;
15539 else if (new_element == EL_SP_DISK_RED)
15540 new_element = EL_SP_DISK_RED_ACTIVE;
15543 Feld[dropx][dropy] = new_element;
15545 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15546 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15547 el2img(Feld[dropx][dropy]), 0);
15549 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15551 /* needed if previous element just changed to "empty" in the last frame */
15552 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15554 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15555 player->index_bit, drop_side);
15556 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15558 player->index_bit, drop_side);
15560 TestIfElementTouchesCustomElement(dropx, dropy);
15562 else /* player is dropping a dyna bomb */
15564 player->dynabombs_left--;
15566 Feld[dropx][dropy] = new_element;
15568 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15569 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15570 el2img(Feld[dropx][dropy]), 0);
15572 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15575 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15576 InitField_WithBug1(dropx, dropy, FALSE);
15578 new_element = Feld[dropx][dropy]; /* element might have changed */
15580 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15581 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15583 int move_direction, nextx, nexty;
15585 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15586 MovDir[dropx][dropy] = drop_direction;
15588 move_direction = MovDir[dropx][dropy];
15589 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15590 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15592 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15594 #if USE_FIX_IMPACT_COLLISION
15595 /* do not cause impact style collision by dropping elements that can fall */
15596 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15598 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15602 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15603 player->is_dropping = TRUE;
15605 player->drop_pressed_delay = 0;
15606 player->is_dropping_pressed = FALSE;
15608 player->drop_x = dropx;
15609 player->drop_y = dropy;
15614 /* ------------------------------------------------------------------------- */
15615 /* game sound playing functions */
15616 /* ------------------------------------------------------------------------- */
15618 static int *loop_sound_frame = NULL;
15619 static int *loop_sound_volume = NULL;
15621 void InitPlayLevelSound()
15623 int num_sounds = getSoundListSize();
15625 checked_free(loop_sound_frame);
15626 checked_free(loop_sound_volume);
15628 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15629 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15632 static void PlayLevelSound(int x, int y, int nr)
15634 int sx = SCREENX(x), sy = SCREENY(y);
15635 int volume, stereo_position;
15636 int max_distance = 8;
15637 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15639 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15640 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15643 if (!IN_LEV_FIELD(x, y) ||
15644 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15645 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15648 volume = SOUND_MAX_VOLUME;
15650 if (!IN_SCR_FIELD(sx, sy))
15652 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15653 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15655 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15658 stereo_position = (SOUND_MAX_LEFT +
15659 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15660 (SCR_FIELDX + 2 * max_distance));
15662 if (IS_LOOP_SOUND(nr))
15664 /* This assures that quieter loop sounds do not overwrite louder ones,
15665 while restarting sound volume comparison with each new game frame. */
15667 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15670 loop_sound_volume[nr] = volume;
15671 loop_sound_frame[nr] = FrameCounter;
15674 PlaySoundExt(nr, volume, stereo_position, type);
15677 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15679 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15680 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15681 y < LEVELY(BY1) ? LEVELY(BY1) :
15682 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15686 static void PlayLevelSoundAction(int x, int y, int action)
15688 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15691 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15693 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15695 if (sound_effect != SND_UNDEFINED)
15696 PlayLevelSound(x, y, sound_effect);
15699 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15702 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15704 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15705 PlayLevelSound(x, y, sound_effect);
15708 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15710 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15712 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15713 PlayLevelSound(x, y, sound_effect);
15716 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15718 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15720 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15721 StopSound(sound_effect);
15724 static void PlayLevelMusic()
15726 if (levelset.music[level_nr] != MUS_UNDEFINED)
15727 PlayMusic(levelset.music[level_nr]); /* from config file */
15729 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15732 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15734 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15735 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15736 int x = xx - 1 - offset;
15737 int y = yy - 1 - offset;
15742 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15746 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15750 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15754 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15758 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15762 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15766 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15769 case SAMPLE_android_clone:
15770 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15773 case SAMPLE_android_move:
15774 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15777 case SAMPLE_spring:
15778 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15782 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15786 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15789 case SAMPLE_eater_eat:
15790 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15794 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15797 case SAMPLE_collect:
15798 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15801 case SAMPLE_diamond:
15802 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15805 case SAMPLE_squash:
15806 /* !!! CHECK THIS !!! */
15808 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15810 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15814 case SAMPLE_wonderfall:
15815 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15819 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15823 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15827 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15831 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15835 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15839 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15842 case SAMPLE_wonder:
15843 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15847 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15850 case SAMPLE_exit_open:
15851 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15854 case SAMPLE_exit_leave:
15855 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15858 case SAMPLE_dynamite:
15859 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15863 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15867 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15871 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15875 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15879 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15883 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15887 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15893 void ChangeTime(int value)
15895 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15899 /* EMC game engine uses value from time counter of RND game engine */
15900 level.native_em_level->lev->time = *time;
15902 DrawGameValue_Time(*time);
15905 void RaiseScore(int value)
15907 /* EMC game engine and RND game engine have separate score counters */
15908 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15909 &level.native_em_level->lev->score : &local_player->score);
15913 DrawGameValue_Score(*score);
15917 void RaiseScore(int value)
15919 local_player->score += value;
15922 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15924 DisplayGameControlValues();
15926 DrawGameValue_Score(local_player->score);
15930 void RaiseScoreElement(int element)
15935 case EL_BD_DIAMOND:
15936 case EL_EMERALD_YELLOW:
15937 case EL_EMERALD_RED:
15938 case EL_EMERALD_PURPLE:
15939 case EL_SP_INFOTRON:
15940 RaiseScore(level.score[SC_EMERALD]);
15943 RaiseScore(level.score[SC_DIAMOND]);
15946 RaiseScore(level.score[SC_CRYSTAL]);
15949 RaiseScore(level.score[SC_PEARL]);
15952 case EL_BD_BUTTERFLY:
15953 case EL_SP_ELECTRON:
15954 RaiseScore(level.score[SC_BUG]);
15957 case EL_BD_FIREFLY:
15958 case EL_SP_SNIKSNAK:
15959 RaiseScore(level.score[SC_SPACESHIP]);
15962 case EL_DARK_YAMYAM:
15963 RaiseScore(level.score[SC_YAMYAM]);
15966 RaiseScore(level.score[SC_ROBOT]);
15969 RaiseScore(level.score[SC_PACMAN]);
15972 RaiseScore(level.score[SC_NUT]);
15975 case EL_EM_DYNAMITE:
15976 case EL_SP_DISK_RED:
15977 case EL_DYNABOMB_INCREASE_NUMBER:
15978 case EL_DYNABOMB_INCREASE_SIZE:
15979 case EL_DYNABOMB_INCREASE_POWER:
15980 RaiseScore(level.score[SC_DYNAMITE]);
15982 case EL_SHIELD_NORMAL:
15983 case EL_SHIELD_DEADLY:
15984 RaiseScore(level.score[SC_SHIELD]);
15986 case EL_EXTRA_TIME:
15987 RaiseScore(level.extra_time_score);
16001 case EL_DC_KEY_WHITE:
16002 RaiseScore(level.score[SC_KEY]);
16005 RaiseScore(element_info[element].collect_score);
16010 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16012 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16014 #if defined(NETWORK_AVALIABLE)
16015 if (options.network)
16016 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16025 FadeSkipNextFadeIn();
16027 fading = fading_none;
16031 OpenDoor(DOOR_CLOSE_1);
16034 game_status = GAME_MODE_MAIN;
16037 DrawAndFadeInMainMenu(REDRAW_FIELD);
16045 FadeOut(REDRAW_FIELD);
16048 game_status = GAME_MODE_MAIN;
16050 DrawAndFadeInMainMenu(REDRAW_FIELD);
16054 else /* continue playing the game */
16056 if (tape.playing && tape.deactivate_display)
16057 TapeDeactivateDisplayOff(TRUE);
16059 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16061 if (tape.playing && tape.deactivate_display)
16062 TapeDeactivateDisplayOn();
16066 void RequestQuitGame(boolean ask_if_really_quit)
16068 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16069 boolean skip_request = AllPlayersGone || quick_quit;
16071 RequestQuitGameExt(skip_request, quick_quit,
16072 "Do you really want to quit the game ?");
16076 /* ------------------------------------------------------------------------- */
16077 /* random generator functions */
16078 /* ------------------------------------------------------------------------- */
16080 unsigned int InitEngineRandom_RND(long seed)
16082 game.num_random_calls = 0;
16085 unsigned int rnd_seed = InitEngineRandom(seed);
16087 printf("::: START RND: %d\n", rnd_seed);
16092 return InitEngineRandom(seed);
16098 unsigned int RND(int max)
16102 game.num_random_calls++;
16104 return GetEngineRandom(max);
16111 /* ------------------------------------------------------------------------- */
16112 /* game engine snapshot handling functions */
16113 /* ------------------------------------------------------------------------- */
16115 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16117 struct EngineSnapshotInfo
16119 /* runtime values for custom element collect score */
16120 int collect_score[NUM_CUSTOM_ELEMENTS];
16122 /* runtime values for group element choice position */
16123 int choice_pos[NUM_GROUP_ELEMENTS];
16125 /* runtime values for belt position animations */
16126 int belt_graphic[4 * NUM_BELT_PARTS];
16127 int belt_anim_mode[4 * NUM_BELT_PARTS];
16130 struct EngineSnapshotNodeInfo
16137 static struct EngineSnapshotInfo engine_snapshot_rnd;
16138 static ListNode *engine_snapshot_list = NULL;
16139 static char *snapshot_level_identifier = NULL;
16140 static int snapshot_level_nr = -1;
16142 void FreeEngineSnapshot()
16144 while (engine_snapshot_list != NULL)
16145 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16148 setString(&snapshot_level_identifier, NULL);
16149 snapshot_level_nr = -1;
16152 static void SaveEngineSnapshotValues_RND()
16154 static int belt_base_active_element[4] =
16156 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16157 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16158 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16159 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16163 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16165 int element = EL_CUSTOM_START + i;
16167 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16170 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16172 int element = EL_GROUP_START + i;
16174 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16177 for (i = 0; i < 4; i++)
16179 for (j = 0; j < NUM_BELT_PARTS; j++)
16181 int element = belt_base_active_element[i] + j;
16182 int graphic = el2img(element);
16183 int anim_mode = graphic_info[graphic].anim_mode;
16185 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16186 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16191 static void LoadEngineSnapshotValues_RND()
16193 unsigned long num_random_calls = game.num_random_calls;
16196 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16198 int element = EL_CUSTOM_START + i;
16200 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16203 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16205 int element = EL_GROUP_START + i;
16207 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16210 for (i = 0; i < 4; i++)
16212 for (j = 0; j < NUM_BELT_PARTS; j++)
16214 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16215 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16217 graphic_info[graphic].anim_mode = anim_mode;
16221 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16223 InitRND(tape.random_seed);
16224 for (i = 0; i < num_random_calls; i++)
16228 if (game.num_random_calls != num_random_calls)
16230 Error(ERR_INFO, "number of random calls out of sync");
16231 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16232 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16233 Error(ERR_EXIT, "this should not happen -- please debug");
16237 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16239 struct EngineSnapshotNodeInfo *bi =
16240 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16242 bi->buffer_orig = buffer;
16243 bi->buffer_copy = checked_malloc(size);
16246 memcpy(bi->buffer_copy, buffer, size);
16248 addNodeToList(&engine_snapshot_list, NULL, bi);
16251 void SaveEngineSnapshot()
16253 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16255 if (level_editor_test_game) /* do not save snapshots from editor */
16258 /* copy some special values to a structure better suited for the snapshot */
16260 SaveEngineSnapshotValues_RND();
16261 SaveEngineSnapshotValues_EM();
16263 /* save values stored in special snapshot structure */
16265 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16266 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16268 /* save further RND engine values */
16270 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16271 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16272 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16274 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16275 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16276 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16277 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16279 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16280 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16281 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16282 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16283 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16285 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16286 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16287 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16289 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16291 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16293 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16294 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16296 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16297 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16298 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16299 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16300 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16301 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16302 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16303 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16304 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16305 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16306 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16307 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16308 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16309 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16310 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16311 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16312 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16313 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16315 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16316 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16318 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16319 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16320 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16322 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16323 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16325 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16326 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16327 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16328 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16329 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16331 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16332 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16334 /* save level identification information */
16336 setString(&snapshot_level_identifier, leveldir_current->identifier);
16337 snapshot_level_nr = level_nr;
16340 ListNode *node = engine_snapshot_list;
16343 while (node != NULL)
16345 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16350 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16354 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16356 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16359 void LoadEngineSnapshot()
16361 ListNode *node = engine_snapshot_list;
16363 if (engine_snapshot_list == NULL)
16366 while (node != NULL)
16368 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16373 /* restore special values from snapshot structure */
16375 LoadEngineSnapshotValues_RND();
16376 LoadEngineSnapshotValues_EM();
16379 boolean CheckEngineSnapshot()
16381 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16382 snapshot_level_nr == level_nr);
16386 /* ---------- new game button stuff ---------------------------------------- */
16388 /* graphic position values for game buttons */
16389 #define GAME_BUTTON_XSIZE 30
16390 #define GAME_BUTTON_YSIZE 30
16391 #define GAME_BUTTON_XPOS 5
16392 #define GAME_BUTTON_YPOS 215
16393 #define SOUND_BUTTON_XPOS 5
16394 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16396 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16397 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16398 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16399 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16400 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16401 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16409 } gamebutton_info[NUM_GAME_BUTTONS] =
16413 &game.button.stop.x, &game.button.stop.y,
16414 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16419 &game.button.pause.x, &game.button.pause.y,
16420 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16421 GAME_CTRL_ID_PAUSE,
16425 &game.button.play.x, &game.button.play.y,
16426 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16431 &game.button.sound_music.x, &game.button.sound_music.y,
16432 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16433 SOUND_CTRL_ID_MUSIC,
16434 "background music on/off"
16437 &game.button.sound_loops.x, &game.button.sound_loops.y,
16438 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16439 SOUND_CTRL_ID_LOOPS,
16440 "sound loops on/off"
16443 &game.button.sound_simple.x,&game.button.sound_simple.y,
16444 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16445 SOUND_CTRL_ID_SIMPLE,
16446 "normal sounds on/off"
16450 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16455 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16456 GAME_CTRL_ID_PAUSE,
16460 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16465 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16466 SOUND_CTRL_ID_MUSIC,
16467 "background music on/off"
16470 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16471 SOUND_CTRL_ID_LOOPS,
16472 "sound loops on/off"
16475 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16476 SOUND_CTRL_ID_SIMPLE,
16477 "normal sounds on/off"
16482 void CreateGameButtons()
16486 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16488 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16489 struct GadgetInfo *gi;
16492 unsigned long event_mask;
16494 int gd_xoffset, gd_yoffset;
16495 int gd_x1, gd_x2, gd_y1, gd_y2;
16498 x = DX + *gamebutton_info[i].x;
16499 y = DY + *gamebutton_info[i].y;
16500 gd_xoffset = gamebutton_info[i].gd_x;
16501 gd_yoffset = gamebutton_info[i].gd_y;
16502 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16503 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16505 if (id == GAME_CTRL_ID_STOP ||
16506 id == GAME_CTRL_ID_PAUSE ||
16507 id == GAME_CTRL_ID_PLAY)
16509 button_type = GD_TYPE_NORMAL_BUTTON;
16511 event_mask = GD_EVENT_RELEASED;
16512 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16513 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16517 button_type = GD_TYPE_CHECK_BUTTON;
16519 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16520 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16521 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16522 event_mask = GD_EVENT_PRESSED;
16523 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16524 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16527 gi = CreateGadget(GDI_CUSTOM_ID, id,
16528 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16533 GDI_X, DX + gd_xoffset,
16534 GDI_Y, DY + gd_yoffset,
16536 GDI_WIDTH, GAME_BUTTON_XSIZE,
16537 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16538 GDI_TYPE, button_type,
16539 GDI_STATE, GD_BUTTON_UNPRESSED,
16540 GDI_CHECKED, checked,
16541 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16542 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16543 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16544 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16545 GDI_DIRECT_DRAW, FALSE,
16546 GDI_EVENT_MASK, event_mask,
16547 GDI_CALLBACK_ACTION, HandleGameButtons,
16551 Error(ERR_EXIT, "cannot create gadget");
16553 game_gadget[id] = gi;
16557 void FreeGameButtons()
16561 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16562 FreeGadget(game_gadget[i]);
16565 static void MapGameButtons()
16569 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16570 MapGadget(game_gadget[i]);
16573 void UnmapGameButtons()
16577 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16578 UnmapGadget(game_gadget[i]);
16581 void RedrawGameButtons()
16585 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16586 RedrawGadget(game_gadget[i]);
16589 static void HandleGameButtons(struct GadgetInfo *gi)
16591 int id = gi->custom_id;
16593 if (game_status != GAME_MODE_PLAYING)
16598 case GAME_CTRL_ID_STOP:
16602 RequestQuitGame(TRUE);
16605 case GAME_CTRL_ID_PAUSE:
16606 if (options.network)
16608 #if defined(NETWORK_AVALIABLE)
16610 SendToServer_ContinuePlaying();
16612 SendToServer_PausePlaying();
16616 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16619 case GAME_CTRL_ID_PLAY:
16622 #if defined(NETWORK_AVALIABLE)
16623 if (options.network)
16624 SendToServer_ContinuePlaying();
16628 tape.pausing = FALSE;
16629 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16634 case SOUND_CTRL_ID_MUSIC:
16635 if (setup.sound_music)
16637 setup.sound_music = FALSE;
16640 else if (audio.music_available)
16642 setup.sound = setup.sound_music = TRUE;
16644 SetAudioMode(setup.sound);
16650 case SOUND_CTRL_ID_LOOPS:
16651 if (setup.sound_loops)
16652 setup.sound_loops = FALSE;
16653 else if (audio.loops_available)
16655 setup.sound = setup.sound_loops = TRUE;
16656 SetAudioMode(setup.sound);
16660 case SOUND_CTRL_ID_SIMPLE:
16661 if (setup.sound_simple)
16662 setup.sound_simple = FALSE;
16663 else if (audio.sound_available)
16665 setup.sound = setup.sound_simple = TRUE;
16666 SetAudioMode(setup.sound);