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 case CA_SET_LEVEL_RANDOM_SEED:
10436 /* ensure that setting a new random seed while playing is predictable */
10437 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10439 InitRND(action_arg_number_new);
10443 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10451 for (i = 0; i < 9; i++)
10452 printf("%d, ", RND(2));
10460 /* ---------- player actions ------------------------------------------ */
10462 case CA_MOVE_PLAYER:
10464 /* automatically move to the next field in specified direction */
10465 for (i = 0; i < MAX_PLAYERS; i++)
10466 if (trigger_player_bits & (1 << i))
10467 stored_player[i].programmed_action = action_arg_direction;
10472 case CA_EXIT_PLAYER:
10474 for (i = 0; i < MAX_PLAYERS; i++)
10475 if (action_arg_player_bits & (1 << i))
10476 PlayerWins(&stored_player[i]);
10481 case CA_KILL_PLAYER:
10483 for (i = 0; i < MAX_PLAYERS; i++)
10484 if (action_arg_player_bits & (1 << i))
10485 KillPlayer(&stored_player[i]);
10490 case CA_SET_PLAYER_KEYS:
10492 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10493 int element = getSpecialActionElement(action_arg_element,
10494 action_arg_number, EL_KEY_1);
10496 if (IS_KEY(element))
10498 for (i = 0; i < MAX_PLAYERS; i++)
10500 if (trigger_player_bits & (1 << i))
10502 stored_player[i].key[KEY_NR(element)] = key_state;
10504 DrawGameDoorValues();
10512 case CA_SET_PLAYER_SPEED:
10515 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10518 for (i = 0; i < MAX_PLAYERS; i++)
10520 if (trigger_player_bits & (1 << i))
10522 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10524 if (action_arg == CA_ARG_SPEED_FASTER &&
10525 stored_player[i].cannot_move)
10527 action_arg_number = STEPSIZE_VERY_SLOW;
10529 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10530 action_arg == CA_ARG_SPEED_FASTER)
10532 action_arg_number = 2;
10533 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10536 else if (action_arg == CA_ARG_NUMBER_RESET)
10538 action_arg_number = level.initial_player_stepsize[i];
10542 getModifiedActionNumber(move_stepsize,
10545 action_arg_number_min,
10546 action_arg_number_max);
10548 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10555 case CA_SET_PLAYER_SHIELD:
10557 for (i = 0; i < MAX_PLAYERS; i++)
10559 if (trigger_player_bits & (1 << i))
10561 if (action_arg == CA_ARG_SHIELD_OFF)
10563 stored_player[i].shield_normal_time_left = 0;
10564 stored_player[i].shield_deadly_time_left = 0;
10566 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10568 stored_player[i].shield_normal_time_left = 999999;
10570 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10572 stored_player[i].shield_normal_time_left = 999999;
10573 stored_player[i].shield_deadly_time_left = 999999;
10581 #if USE_PLAYER_GRAVITY
10582 case CA_SET_PLAYER_GRAVITY:
10584 for (i = 0; i < MAX_PLAYERS; i++)
10586 if (trigger_player_bits & (1 << i))
10588 stored_player[i].gravity =
10589 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10590 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10591 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10592 stored_player[i].gravity);
10600 case CA_SET_PLAYER_ARTWORK:
10602 for (i = 0; i < MAX_PLAYERS; i++)
10604 if (trigger_player_bits & (1 << i))
10606 int artwork_element = action_arg_element;
10608 if (action_arg == CA_ARG_ELEMENT_RESET)
10610 (level.use_artwork_element[i] ? level.artwork_element[i] :
10611 stored_player[i].element_nr);
10613 #if USE_GFX_RESET_PLAYER_ARTWORK
10614 if (stored_player[i].artwork_element != artwork_element)
10615 stored_player[i].Frame = 0;
10618 stored_player[i].artwork_element = artwork_element;
10620 SetPlayerWaiting(&stored_player[i], FALSE);
10622 /* set number of special actions for bored and sleeping animation */
10623 stored_player[i].num_special_action_bored =
10624 get_num_special_action(artwork_element,
10625 ACTION_BORING_1, ACTION_BORING_LAST);
10626 stored_player[i].num_special_action_sleeping =
10627 get_num_special_action(artwork_element,
10628 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10635 case CA_SET_PLAYER_INVENTORY:
10637 for (i = 0; i < MAX_PLAYERS; i++)
10639 struct PlayerInfo *player = &stored_player[i];
10642 if (trigger_player_bits & (1 << i))
10644 int inventory_element = action_arg_element;
10646 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10647 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10648 action_arg == CA_ARG_ELEMENT_ACTION)
10650 int element = inventory_element;
10651 int collect_count = element_info[element].collect_count_initial;
10653 if (!IS_CUSTOM_ELEMENT(element))
10656 if (collect_count == 0)
10657 player->inventory_infinite_element = element;
10659 for (k = 0; k < collect_count; k++)
10660 if (player->inventory_size < MAX_INVENTORY_SIZE)
10661 player->inventory_element[player->inventory_size++] =
10664 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10665 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10666 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10668 if (player->inventory_infinite_element != EL_UNDEFINED &&
10669 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10670 action_arg_element_raw))
10671 player->inventory_infinite_element = EL_UNDEFINED;
10673 for (k = 0, j = 0; j < player->inventory_size; j++)
10675 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10676 action_arg_element_raw))
10677 player->inventory_element[k++] = player->inventory_element[j];
10680 player->inventory_size = k;
10682 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10684 if (player->inventory_size > 0)
10686 for (j = 0; j < player->inventory_size - 1; j++)
10687 player->inventory_element[j] = player->inventory_element[j + 1];
10689 player->inventory_size--;
10692 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10694 if (player->inventory_size > 0)
10695 player->inventory_size--;
10697 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10699 player->inventory_infinite_element = EL_UNDEFINED;
10700 player->inventory_size = 0;
10702 else if (action_arg == CA_ARG_INVENTORY_RESET)
10704 player->inventory_infinite_element = EL_UNDEFINED;
10705 player->inventory_size = 0;
10707 if (level.use_initial_inventory[i])
10709 for (j = 0; j < level.initial_inventory_size[i]; j++)
10711 int element = level.initial_inventory_content[i][j];
10712 int collect_count = element_info[element].collect_count_initial;
10714 if (!IS_CUSTOM_ELEMENT(element))
10717 if (collect_count == 0)
10718 player->inventory_infinite_element = element;
10720 for (k = 0; k < collect_count; k++)
10721 if (player->inventory_size < MAX_INVENTORY_SIZE)
10722 player->inventory_element[player->inventory_size++] =
10733 /* ---------- CE actions ---------------------------------------------- */
10735 case CA_SET_CE_VALUE:
10737 #if USE_NEW_CUSTOM_VALUE
10738 int last_ce_value = CustomValue[x][y];
10740 CustomValue[x][y] = action_arg_number_new;
10742 if (CustomValue[x][y] != last_ce_value)
10744 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10745 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10747 if (CustomValue[x][y] == 0)
10749 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10750 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10758 case CA_SET_CE_SCORE:
10760 #if USE_NEW_CUSTOM_VALUE
10761 int last_ce_score = ei->collect_score;
10763 ei->collect_score = action_arg_number_new;
10765 if (ei->collect_score != last_ce_score)
10767 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10768 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10770 if (ei->collect_score == 0)
10774 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10775 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10778 This is a very special case that seems to be a mixture between
10779 CheckElementChange() and CheckTriggeredElementChange(): while
10780 the first one only affects single elements that are triggered
10781 directly, the second one affects multiple elements in the playfield
10782 that are triggered indirectly by another element. This is a third
10783 case: Changing the CE score always affects multiple identical CEs,
10784 so every affected CE must be checked, not only the single CE for
10785 which the CE score was changed in the first place (as every instance
10786 of that CE shares the same CE score, and therefore also can change)!
10788 SCAN_PLAYFIELD(xx, yy)
10790 if (Feld[xx][yy] == element)
10791 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10792 CE_SCORE_GETS_ZERO);
10801 case CA_SET_CE_ARTWORK:
10803 int artwork_element = action_arg_element;
10804 boolean reset_frame = FALSE;
10807 if (action_arg == CA_ARG_ELEMENT_RESET)
10808 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10811 if (ei->gfx_element != artwork_element)
10812 reset_frame = TRUE;
10814 ei->gfx_element = artwork_element;
10816 SCAN_PLAYFIELD(xx, yy)
10818 if (Feld[xx][yy] == element)
10822 ResetGfxAnimation(xx, yy);
10823 ResetRandomAnimationValue(xx, yy);
10826 TEST_DrawLevelField(xx, yy);
10833 /* ---------- engine actions ------------------------------------------ */
10835 case CA_SET_ENGINE_SCAN_MODE:
10837 InitPlayfieldScanMode(action_arg);
10847 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10849 int old_element = Feld[x][y];
10850 int new_element = GetElementFromGroupElement(element);
10851 int previous_move_direction = MovDir[x][y];
10852 #if USE_NEW_CUSTOM_VALUE
10853 int last_ce_value = CustomValue[x][y];
10855 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10856 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10857 boolean add_player_onto_element = (new_element_is_player &&
10858 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10859 /* this breaks SnakeBite when a snake is
10860 halfway through a door that closes */
10861 /* NOW FIXED AT LEVEL INIT IN files.c */
10862 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10864 IS_WALKABLE(old_element));
10867 /* check if element under the player changes from accessible to unaccessible
10868 (needed for special case of dropping element which then changes) */
10869 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10870 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10878 if (!add_player_onto_element)
10880 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10881 RemoveMovingField(x, y);
10885 Feld[x][y] = new_element;
10887 #if !USE_GFX_RESET_GFX_ANIMATION
10888 ResetGfxAnimation(x, y);
10889 ResetRandomAnimationValue(x, y);
10892 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10893 MovDir[x][y] = previous_move_direction;
10895 #if USE_NEW_CUSTOM_VALUE
10896 if (element_info[new_element].use_last_ce_value)
10897 CustomValue[x][y] = last_ce_value;
10900 InitField_WithBug1(x, y, FALSE);
10902 new_element = Feld[x][y]; /* element may have changed */
10904 #if USE_GFX_RESET_GFX_ANIMATION
10905 ResetGfxAnimation(x, y);
10906 ResetRandomAnimationValue(x, y);
10909 TEST_DrawLevelField(x, y);
10911 if (GFX_CRUMBLED(new_element))
10912 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
10916 /* check if element under the player changes from accessible to unaccessible
10917 (needed for special case of dropping element which then changes) */
10918 /* (must be checked after creating new element for walkable group elements) */
10919 #if USE_FIX_KILLED_BY_NON_WALKABLE
10920 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10921 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10928 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10929 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10938 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10939 if (new_element_is_player)
10940 RelocatePlayer(x, y, new_element);
10943 ChangeCount[x][y]++; /* count number of changes in the same frame */
10945 TestIfBadThingTouchesPlayer(x, y);
10946 TestIfPlayerTouchesCustomElement(x, y);
10947 TestIfElementTouchesCustomElement(x, y);
10950 static void CreateField(int x, int y, int element)
10952 CreateFieldExt(x, y, element, FALSE);
10955 static void CreateElementFromChange(int x, int y, int element)
10957 element = GET_VALID_RUNTIME_ELEMENT(element);
10959 #if USE_STOP_CHANGED_ELEMENTS
10960 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10962 int old_element = Feld[x][y];
10964 /* prevent changed element from moving in same engine frame
10965 unless both old and new element can either fall or move */
10966 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10967 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10972 CreateFieldExt(x, y, element, TRUE);
10975 static boolean ChangeElement(int x, int y, int element, int page)
10977 struct ElementInfo *ei = &element_info[element];
10978 struct ElementChangeInfo *change = &ei->change_page[page];
10979 int ce_value = CustomValue[x][y];
10980 int ce_score = ei->collect_score;
10981 int target_element;
10982 int old_element = Feld[x][y];
10984 /* always use default change event to prevent running into a loop */
10985 if (ChangeEvent[x][y] == -1)
10986 ChangeEvent[x][y] = CE_DELAY;
10988 if (ChangeEvent[x][y] == CE_DELAY)
10990 /* reset actual trigger element, trigger player and action element */
10991 change->actual_trigger_element = EL_EMPTY;
10992 change->actual_trigger_player = EL_EMPTY;
10993 change->actual_trigger_player_bits = CH_PLAYER_NONE;
10994 change->actual_trigger_side = CH_SIDE_NONE;
10995 change->actual_trigger_ce_value = 0;
10996 change->actual_trigger_ce_score = 0;
10999 /* do not change elements more than a specified maximum number of changes */
11000 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11003 ChangeCount[x][y]++; /* count number of changes in the same frame */
11005 if (change->explode)
11012 if (change->use_target_content)
11014 boolean complete_replace = TRUE;
11015 boolean can_replace[3][3];
11018 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11021 boolean is_walkable;
11022 boolean is_diggable;
11023 boolean is_collectible;
11024 boolean is_removable;
11025 boolean is_destructible;
11026 int ex = x + xx - 1;
11027 int ey = y + yy - 1;
11028 int content_element = change->target_content.e[xx][yy];
11031 can_replace[xx][yy] = TRUE;
11033 if (ex == x && ey == y) /* do not check changing element itself */
11036 if (content_element == EL_EMPTY_SPACE)
11038 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11043 if (!IN_LEV_FIELD(ex, ey))
11045 can_replace[xx][yy] = FALSE;
11046 complete_replace = FALSE;
11053 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11054 e = MovingOrBlocked2Element(ex, ey);
11056 is_empty = (IS_FREE(ex, ey) ||
11057 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11059 is_walkable = (is_empty || IS_WALKABLE(e));
11060 is_diggable = (is_empty || IS_DIGGABLE(e));
11061 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11062 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11063 is_removable = (is_diggable || is_collectible);
11065 can_replace[xx][yy] =
11066 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11067 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11068 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11069 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11070 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11071 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11072 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11074 if (!can_replace[xx][yy])
11075 complete_replace = FALSE;
11078 if (!change->only_if_complete || complete_replace)
11080 boolean something_has_changed = FALSE;
11082 if (change->only_if_complete && change->use_random_replace &&
11083 RND(100) < change->random_percentage)
11086 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11088 int ex = x + xx - 1;
11089 int ey = y + yy - 1;
11090 int content_element;
11092 if (can_replace[xx][yy] && (!change->use_random_replace ||
11093 RND(100) < change->random_percentage))
11095 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11096 RemoveMovingField(ex, ey);
11098 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11100 content_element = change->target_content.e[xx][yy];
11101 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11102 ce_value, ce_score);
11104 CreateElementFromChange(ex, ey, target_element);
11106 something_has_changed = TRUE;
11108 /* for symmetry reasons, freeze newly created border elements */
11109 if (ex != x || ey != y)
11110 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11114 if (something_has_changed)
11116 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11117 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11123 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11124 ce_value, ce_score);
11126 if (element == EL_DIAGONAL_GROWING ||
11127 element == EL_DIAGONAL_SHRINKING)
11129 target_element = Store[x][y];
11131 Store[x][y] = EL_EMPTY;
11134 CreateElementFromChange(x, y, target_element);
11136 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11137 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11140 /* this uses direct change before indirect change */
11141 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11146 #if USE_NEW_DELAYED_ACTION
11148 static void HandleElementChange(int x, int y, int page)
11150 int element = MovingOrBlocked2Element(x, y);
11151 struct ElementInfo *ei = &element_info[element];
11152 struct ElementChangeInfo *change = &ei->change_page[page];
11153 boolean handle_action_before_change = FALSE;
11156 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11157 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11160 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11161 x, y, element, element_info[element].token_name);
11162 printf("HandleElementChange(): This should never happen!\n");
11167 /* this can happen with classic bombs on walkable, changing elements */
11168 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11171 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11172 ChangeDelay[x][y] = 0;
11178 if (ChangeDelay[x][y] == 0) /* initialize element change */
11180 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11182 if (change->can_change)
11185 /* !!! not clear why graphic animation should be reset at all here !!! */
11186 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11187 #if USE_GFX_RESET_WHEN_NOT_MOVING
11188 /* when a custom element is about to change (for example by change delay),
11189 do not reset graphic animation when the custom element is moving */
11190 if (!IS_MOVING(x, y))
11193 ResetGfxAnimation(x, y);
11194 ResetRandomAnimationValue(x, y);
11198 if (change->pre_change_function)
11199 change->pre_change_function(x, y);
11203 ChangeDelay[x][y]--;
11205 if (ChangeDelay[x][y] != 0) /* continue element change */
11207 if (change->can_change)
11209 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11211 if (IS_ANIMATED(graphic))
11212 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11214 if (change->change_function)
11215 change->change_function(x, y);
11218 else /* finish element change */
11220 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11222 page = ChangePage[x][y];
11223 ChangePage[x][y] = -1;
11225 change = &ei->change_page[page];
11228 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11230 ChangeDelay[x][y] = 1; /* try change after next move step */
11231 ChangePage[x][y] = page; /* remember page to use for change */
11237 /* special case: set new level random seed before changing element */
11238 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11239 handle_action_before_change = TRUE;
11241 if (change->has_action && handle_action_before_change)
11242 ExecuteCustomElementAction(x, y, element, page);
11245 if (change->can_change)
11247 if (ChangeElement(x, y, element, page))
11249 if (change->post_change_function)
11250 change->post_change_function(x, y);
11254 if (change->has_action && !handle_action_before_change)
11255 ExecuteCustomElementAction(x, y, element, page);
11261 static void HandleElementChange(int x, int y, int page)
11263 int element = MovingOrBlocked2Element(x, y);
11264 struct ElementInfo *ei = &element_info[element];
11265 struct ElementChangeInfo *change = &ei->change_page[page];
11268 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11271 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11272 x, y, element, element_info[element].token_name);
11273 printf("HandleElementChange(): This should never happen!\n");
11278 /* this can happen with classic bombs on walkable, changing elements */
11279 if (!CAN_CHANGE(element))
11282 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11283 ChangeDelay[x][y] = 0;
11289 if (ChangeDelay[x][y] == 0) /* initialize element change */
11291 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11293 ResetGfxAnimation(x, y);
11294 ResetRandomAnimationValue(x, y);
11296 if (change->pre_change_function)
11297 change->pre_change_function(x, y);
11300 ChangeDelay[x][y]--;
11302 if (ChangeDelay[x][y] != 0) /* continue element change */
11304 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11306 if (IS_ANIMATED(graphic))
11307 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11309 if (change->change_function)
11310 change->change_function(x, y);
11312 else /* finish element change */
11314 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11316 page = ChangePage[x][y];
11317 ChangePage[x][y] = -1;
11319 change = &ei->change_page[page];
11322 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11324 ChangeDelay[x][y] = 1; /* try change after next move step */
11325 ChangePage[x][y] = page; /* remember page to use for change */
11330 if (ChangeElement(x, y, element, page))
11332 if (change->post_change_function)
11333 change->post_change_function(x, y);
11340 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11341 int trigger_element,
11343 int trigger_player,
11347 boolean change_done_any = FALSE;
11348 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11351 if (!(trigger_events[trigger_element][trigger_event]))
11355 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11356 trigger_event, recursion_loop_depth, recursion_loop_detected,
11357 recursion_loop_element, EL_NAME(recursion_loop_element));
11360 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11362 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11364 int element = EL_CUSTOM_START + i;
11365 boolean change_done = FALSE;
11368 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11369 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11372 for (p = 0; p < element_info[element].num_change_pages; p++)
11374 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11376 if (change->can_change_or_has_action &&
11377 change->has_event[trigger_event] &&
11378 change->trigger_side & trigger_side &&
11379 change->trigger_player & trigger_player &&
11380 change->trigger_page & trigger_page_bits &&
11381 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11383 change->actual_trigger_element = trigger_element;
11384 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11385 change->actual_trigger_player_bits = trigger_player;
11386 change->actual_trigger_side = trigger_side;
11387 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11388 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11391 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11392 element, EL_NAME(element), p);
11395 if ((change->can_change && !change_done) || change->has_action)
11399 SCAN_PLAYFIELD(x, y)
11401 if (Feld[x][y] == element)
11403 if (change->can_change && !change_done)
11405 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11406 /* if element already changed in this frame, not only prevent
11407 another element change (checked in ChangeElement()), but
11408 also prevent additional element actions for this element */
11410 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11411 !level.use_action_after_change_bug)
11416 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11417 element, EL_NAME(element), p);
11420 ChangeDelay[x][y] = 1;
11421 ChangeEvent[x][y] = trigger_event;
11423 HandleElementChange(x, y, p);
11425 #if USE_NEW_DELAYED_ACTION
11426 else if (change->has_action)
11428 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11429 /* if element already changed in this frame, not only prevent
11430 another element change (checked in ChangeElement()), but
11431 also prevent additional element actions for this element */
11433 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11434 !level.use_action_after_change_bug)
11440 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11441 element, EL_NAME(element), p);
11444 ExecuteCustomElementAction(x, y, element, p);
11445 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11448 if (change->has_action)
11450 ExecuteCustomElementAction(x, y, element, p);
11451 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11457 if (change->can_change)
11459 change_done = TRUE;
11460 change_done_any = TRUE;
11463 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11464 element, EL_NAME(element), p);
11473 RECURSION_LOOP_DETECTION_END();
11475 return change_done_any;
11478 static boolean CheckElementChangeExt(int x, int y,
11480 int trigger_element,
11482 int trigger_player,
11485 boolean change_done = FALSE;
11488 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11489 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11492 if (Feld[x][y] == EL_BLOCKED)
11494 Blocked2Moving(x, y, &x, &y);
11495 element = Feld[x][y];
11499 /* check if element has already changed */
11500 if (Feld[x][y] != element)
11503 /* check if element has already changed or is about to change after moving */
11504 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11505 Feld[x][y] != element) ||
11507 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11508 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11509 ChangePage[x][y] != -1)))
11514 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11515 trigger_event, recursion_loop_depth, recursion_loop_detected,
11516 recursion_loop_element, EL_NAME(recursion_loop_element));
11519 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11522 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11525 for (p = 0; p < element_info[element].num_change_pages; p++)
11527 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11529 /* check trigger element for all events where the element that is checked
11530 for changing interacts with a directly adjacent element -- this is
11531 different to element changes that affect other elements to change on the
11532 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11533 boolean check_trigger_element =
11534 (trigger_event == CE_TOUCHING_X ||
11535 trigger_event == CE_HITTING_X ||
11536 trigger_event == CE_HIT_BY_X ||
11538 /* this one was forgotten until 3.2.3 */
11539 trigger_event == CE_DIGGING_X);
11542 if (change->can_change_or_has_action &&
11543 change->has_event[trigger_event] &&
11544 change->trigger_side & trigger_side &&
11545 change->trigger_player & trigger_player &&
11546 (!check_trigger_element ||
11547 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11549 change->actual_trigger_element = trigger_element;
11550 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11551 change->actual_trigger_player_bits = trigger_player;
11552 change->actual_trigger_side = trigger_side;
11553 change->actual_trigger_ce_value = CustomValue[x][y];
11554 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11556 /* special case: trigger element not at (x,y) position for some events */
11557 if (check_trigger_element)
11569 { 0, 0 }, { 0, 0 }, { 0, 0 },
11573 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11574 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11576 change->actual_trigger_ce_value = CustomValue[xx][yy];
11577 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11580 if (change->can_change && !change_done)
11582 ChangeDelay[x][y] = 1;
11583 ChangeEvent[x][y] = trigger_event;
11585 HandleElementChange(x, y, p);
11587 change_done = TRUE;
11589 #if USE_NEW_DELAYED_ACTION
11590 else if (change->has_action)
11592 ExecuteCustomElementAction(x, y, element, p);
11593 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11596 if (change->has_action)
11598 ExecuteCustomElementAction(x, y, element, p);
11599 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11605 RECURSION_LOOP_DETECTION_END();
11607 return change_done;
11610 static void PlayPlayerSound(struct PlayerInfo *player)
11612 int jx = player->jx, jy = player->jy;
11613 int sound_element = player->artwork_element;
11614 int last_action = player->last_action_waiting;
11615 int action = player->action_waiting;
11617 if (player->is_waiting)
11619 if (action != last_action)
11620 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11622 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11626 if (action != last_action)
11627 StopSound(element_info[sound_element].sound[last_action]);
11629 if (last_action == ACTION_SLEEPING)
11630 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11634 static void PlayAllPlayersSound()
11638 for (i = 0; i < MAX_PLAYERS; i++)
11639 if (stored_player[i].active)
11640 PlayPlayerSound(&stored_player[i]);
11643 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11645 boolean last_waiting = player->is_waiting;
11646 int move_dir = player->MovDir;
11648 player->dir_waiting = move_dir;
11649 player->last_action_waiting = player->action_waiting;
11653 if (!last_waiting) /* not waiting -> waiting */
11655 player->is_waiting = TRUE;
11657 player->frame_counter_bored =
11659 game.player_boring_delay_fixed +
11660 GetSimpleRandom(game.player_boring_delay_random);
11661 player->frame_counter_sleeping =
11663 game.player_sleeping_delay_fixed +
11664 GetSimpleRandom(game.player_sleeping_delay_random);
11666 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11669 if (game.player_sleeping_delay_fixed +
11670 game.player_sleeping_delay_random > 0 &&
11671 player->anim_delay_counter == 0 &&
11672 player->post_delay_counter == 0 &&
11673 FrameCounter >= player->frame_counter_sleeping)
11674 player->is_sleeping = TRUE;
11675 else if (game.player_boring_delay_fixed +
11676 game.player_boring_delay_random > 0 &&
11677 FrameCounter >= player->frame_counter_bored)
11678 player->is_bored = TRUE;
11680 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11681 player->is_bored ? ACTION_BORING :
11684 if (player->is_sleeping && player->use_murphy)
11686 /* special case for sleeping Murphy when leaning against non-free tile */
11688 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11689 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11690 !IS_MOVING(player->jx - 1, player->jy)))
11691 move_dir = MV_LEFT;
11692 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11693 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11694 !IS_MOVING(player->jx + 1, player->jy)))
11695 move_dir = MV_RIGHT;
11697 player->is_sleeping = FALSE;
11699 player->dir_waiting = move_dir;
11702 if (player->is_sleeping)
11704 if (player->num_special_action_sleeping > 0)
11706 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11708 int last_special_action = player->special_action_sleeping;
11709 int num_special_action = player->num_special_action_sleeping;
11710 int special_action =
11711 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11712 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11713 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11714 last_special_action + 1 : ACTION_SLEEPING);
11715 int special_graphic =
11716 el_act_dir2img(player->artwork_element, special_action, move_dir);
11718 player->anim_delay_counter =
11719 graphic_info[special_graphic].anim_delay_fixed +
11720 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11721 player->post_delay_counter =
11722 graphic_info[special_graphic].post_delay_fixed +
11723 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11725 player->special_action_sleeping = special_action;
11728 if (player->anim_delay_counter > 0)
11730 player->action_waiting = player->special_action_sleeping;
11731 player->anim_delay_counter--;
11733 else if (player->post_delay_counter > 0)
11735 player->post_delay_counter--;
11739 else if (player->is_bored)
11741 if (player->num_special_action_bored > 0)
11743 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11745 int special_action =
11746 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11747 int special_graphic =
11748 el_act_dir2img(player->artwork_element, special_action, move_dir);
11750 player->anim_delay_counter =
11751 graphic_info[special_graphic].anim_delay_fixed +
11752 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11753 player->post_delay_counter =
11754 graphic_info[special_graphic].post_delay_fixed +
11755 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11757 player->special_action_bored = special_action;
11760 if (player->anim_delay_counter > 0)
11762 player->action_waiting = player->special_action_bored;
11763 player->anim_delay_counter--;
11765 else if (player->post_delay_counter > 0)
11767 player->post_delay_counter--;
11772 else if (last_waiting) /* waiting -> not waiting */
11774 player->is_waiting = FALSE;
11775 player->is_bored = FALSE;
11776 player->is_sleeping = FALSE;
11778 player->frame_counter_bored = -1;
11779 player->frame_counter_sleeping = -1;
11781 player->anim_delay_counter = 0;
11782 player->post_delay_counter = 0;
11784 player->dir_waiting = player->MovDir;
11785 player->action_waiting = ACTION_DEFAULT;
11787 player->special_action_bored = ACTION_DEFAULT;
11788 player->special_action_sleeping = ACTION_DEFAULT;
11792 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11794 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11795 int left = player_action & JOY_LEFT;
11796 int right = player_action & JOY_RIGHT;
11797 int up = player_action & JOY_UP;
11798 int down = player_action & JOY_DOWN;
11799 int button1 = player_action & JOY_BUTTON_1;
11800 int button2 = player_action & JOY_BUTTON_2;
11801 int dx = (left ? -1 : right ? 1 : 0);
11802 int dy = (up ? -1 : down ? 1 : 0);
11804 if (!player->active || tape.pausing)
11810 snapped = SnapField(player, dx, dy);
11814 dropped = DropElement(player);
11816 moved = MovePlayer(player, dx, dy);
11819 if (tape.single_step && tape.recording && !tape.pausing)
11821 if (button1 || (dropped && !moved))
11823 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11824 SnapField(player, 0, 0); /* stop snapping */
11828 SetPlayerWaiting(player, FALSE);
11830 return player_action;
11834 /* no actions for this player (no input at player's configured device) */
11836 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11837 SnapField(player, 0, 0);
11838 CheckGravityMovementWhenNotMoving(player);
11840 if (player->MovPos == 0)
11841 SetPlayerWaiting(player, TRUE);
11843 if (player->MovPos == 0) /* needed for tape.playing */
11844 player->is_moving = FALSE;
11846 player->is_dropping = FALSE;
11847 player->is_dropping_pressed = FALSE;
11848 player->drop_pressed_delay = 0;
11854 static void CheckLevelTime()
11858 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11860 if (level.native_em_level->lev->home == 0) /* all players at home */
11862 PlayerWins(local_player);
11864 AllPlayersGone = TRUE;
11866 level.native_em_level->lev->home = -1;
11869 if (level.native_em_level->ply[0]->alive == 0 &&
11870 level.native_em_level->ply[1]->alive == 0 &&
11871 level.native_em_level->ply[2]->alive == 0 &&
11872 level.native_em_level->ply[3]->alive == 0) /* all dead */
11873 AllPlayersGone = TRUE;
11876 if (TimeFrames >= FRAMES_PER_SECOND)
11881 for (i = 0; i < MAX_PLAYERS; i++)
11883 struct PlayerInfo *player = &stored_player[i];
11885 if (SHIELD_ON(player))
11887 player->shield_normal_time_left--;
11889 if (player->shield_deadly_time_left > 0)
11890 player->shield_deadly_time_left--;
11894 if (!local_player->LevelSolved && !level.use_step_counter)
11902 if (TimeLeft <= 10 && setup.time_limit)
11903 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11906 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11908 DisplayGameControlValues();
11910 DrawGameValue_Time(TimeLeft);
11913 if (!TimeLeft && setup.time_limit)
11915 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11916 level.native_em_level->lev->killed_out_of_time = TRUE;
11918 for (i = 0; i < MAX_PLAYERS; i++)
11919 KillPlayer(&stored_player[i]);
11923 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11925 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11927 DisplayGameControlValues();
11930 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11931 DrawGameValue_Time(TimePlayed);
11934 level.native_em_level->lev->time =
11935 (level.time == 0 ? TimePlayed : TimeLeft);
11938 if (tape.recording || tape.playing)
11939 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11943 UpdateAndDisplayGameControlValues();
11945 UpdateGameDoorValues();
11946 DrawGameDoorValues();
11950 void AdvanceFrameAndPlayerCounters(int player_nr)
11954 /* advance frame counters (global frame counter and time frame counter) */
11958 /* advance player counters (counters for move delay, move animation etc.) */
11959 for (i = 0; i < MAX_PLAYERS; i++)
11961 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11962 int move_delay_value = stored_player[i].move_delay_value;
11963 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11965 if (!advance_player_counters) /* not all players may be affected */
11968 #if USE_NEW_PLAYER_ANIM
11969 if (move_frames == 0) /* less than one move per game frame */
11971 int stepsize = TILEX / move_delay_value;
11972 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11973 int count = (stored_player[i].is_moving ?
11974 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11976 if (count % delay == 0)
11981 stored_player[i].Frame += move_frames;
11983 if (stored_player[i].MovPos != 0)
11984 stored_player[i].StepFrame += move_frames;
11986 if (stored_player[i].move_delay > 0)
11987 stored_player[i].move_delay--;
11989 /* due to bugs in previous versions, counter must count up, not down */
11990 if (stored_player[i].push_delay != -1)
11991 stored_player[i].push_delay++;
11993 if (stored_player[i].drop_delay > 0)
11994 stored_player[i].drop_delay--;
11996 if (stored_player[i].is_dropping_pressed)
11997 stored_player[i].drop_pressed_delay++;
12001 void StartGameActions(boolean init_network_game, boolean record_tape,
12004 unsigned long new_random_seed = InitRND(random_seed);
12007 TapeStartRecording(new_random_seed);
12009 #if defined(NETWORK_AVALIABLE)
12010 if (init_network_game)
12012 SendToServer_StartPlaying();
12023 static unsigned long game_frame_delay = 0;
12024 unsigned long game_frame_delay_value;
12025 byte *recorded_player_action;
12026 byte summarized_player_action = 0;
12027 byte tape_action[MAX_PLAYERS];
12030 /* detect endless loops, caused by custom element programming */
12031 if (recursion_loop_detected && recursion_loop_depth == 0)
12033 char *message = getStringCat3("Internal Error ! Element ",
12034 EL_NAME(recursion_loop_element),
12035 " caused endless loop ! Quit the game ?");
12037 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12038 EL_NAME(recursion_loop_element));
12040 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12042 recursion_loop_detected = FALSE; /* if game should be continued */
12049 if (game.restart_level)
12050 StartGameActions(options.network, setup.autorecord, level.random_seed);
12052 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12054 if (level.native_em_level->lev->home == 0) /* all players at home */
12056 PlayerWins(local_player);
12058 AllPlayersGone = TRUE;
12060 level.native_em_level->lev->home = -1;
12063 if (level.native_em_level->ply[0]->alive == 0 &&
12064 level.native_em_level->ply[1]->alive == 0 &&
12065 level.native_em_level->ply[2]->alive == 0 &&
12066 level.native_em_level->ply[3]->alive == 0) /* all dead */
12067 AllPlayersGone = TRUE;
12070 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12073 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12076 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12079 game_frame_delay_value =
12080 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12082 if (tape.playing && tape.warp_forward && !tape.pausing)
12083 game_frame_delay_value = 0;
12085 /* ---------- main game synchronization point ---------- */
12087 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12089 if (network_playing && !network_player_action_received)
12091 /* try to get network player actions in time */
12093 #if defined(NETWORK_AVALIABLE)
12094 /* last chance to get network player actions without main loop delay */
12095 HandleNetworking();
12098 /* game was quit by network peer */
12099 if (game_status != GAME_MODE_PLAYING)
12102 if (!network_player_action_received)
12103 return; /* failed to get network player actions in time */
12105 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12111 /* at this point we know that we really continue executing the game */
12113 network_player_action_received = FALSE;
12115 /* when playing tape, read previously recorded player input from tape data */
12116 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12119 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12124 if (tape.set_centered_player)
12126 game.centered_player_nr_next = tape.centered_player_nr_next;
12127 game.set_centered_player = TRUE;
12130 for (i = 0; i < MAX_PLAYERS; i++)
12132 summarized_player_action |= stored_player[i].action;
12134 if (!network_playing)
12135 stored_player[i].effective_action = stored_player[i].action;
12138 #if defined(NETWORK_AVALIABLE)
12139 if (network_playing)
12140 SendToServer_MovePlayer(summarized_player_action);
12143 if (!options.network && !setup.team_mode)
12144 local_player->effective_action = summarized_player_action;
12146 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12148 for (i = 0; i < MAX_PLAYERS; i++)
12149 stored_player[i].effective_action =
12150 (i == game.centered_player_nr ? summarized_player_action : 0);
12153 if (recorded_player_action != NULL)
12154 for (i = 0; i < MAX_PLAYERS; i++)
12155 stored_player[i].effective_action = recorded_player_action[i];
12157 for (i = 0; i < MAX_PLAYERS; i++)
12159 tape_action[i] = stored_player[i].effective_action;
12161 /* (this can only happen in the R'n'D game engine) */
12162 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12163 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12166 /* only record actions from input devices, but not programmed actions */
12167 if (tape.recording)
12168 TapeRecordAction(tape_action);
12170 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12172 GameActions_EM_Main();
12180 void GameActions_EM_Main()
12182 byte effective_action[MAX_PLAYERS];
12183 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12186 for (i = 0; i < MAX_PLAYERS; i++)
12187 effective_action[i] = stored_player[i].effective_action;
12189 GameActions_EM(effective_action, warp_mode);
12193 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12196 void GameActions_RND()
12198 int magic_wall_x = 0, magic_wall_y = 0;
12199 int i, x, y, element, graphic;
12201 InitPlayfieldScanModeVars();
12203 #if USE_ONE_MORE_CHANGE_PER_FRAME
12204 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12206 SCAN_PLAYFIELD(x, y)
12208 ChangeCount[x][y] = 0;
12209 ChangeEvent[x][y] = -1;
12214 if (game.set_centered_player)
12216 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12218 /* switching to "all players" only possible if all players fit to screen */
12219 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12221 game.centered_player_nr_next = game.centered_player_nr;
12222 game.set_centered_player = FALSE;
12225 /* do not switch focus to non-existing (or non-active) player */
12226 if (game.centered_player_nr_next >= 0 &&
12227 !stored_player[game.centered_player_nr_next].active)
12229 game.centered_player_nr_next = game.centered_player_nr;
12230 game.set_centered_player = FALSE;
12234 if (game.set_centered_player &&
12235 ScreenMovPos == 0) /* screen currently aligned at tile position */
12239 if (game.centered_player_nr_next == -1)
12241 setScreenCenteredToAllPlayers(&sx, &sy);
12245 sx = stored_player[game.centered_player_nr_next].jx;
12246 sy = stored_player[game.centered_player_nr_next].jy;
12249 game.centered_player_nr = game.centered_player_nr_next;
12250 game.set_centered_player = FALSE;
12252 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12253 DrawGameDoorValues();
12256 for (i = 0; i < MAX_PLAYERS; i++)
12258 int actual_player_action = stored_player[i].effective_action;
12261 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12262 - rnd_equinox_tetrachloride 048
12263 - rnd_equinox_tetrachloride_ii 096
12264 - rnd_emanuel_schmieg 002
12265 - doctor_sloan_ww 001, 020
12267 if (stored_player[i].MovPos == 0)
12268 CheckGravityMovement(&stored_player[i]);
12271 /* overwrite programmed action with tape action */
12272 if (stored_player[i].programmed_action)
12273 actual_player_action = stored_player[i].programmed_action;
12275 PlayerActions(&stored_player[i], actual_player_action);
12277 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12280 ScrollScreen(NULL, SCROLL_GO_ON);
12282 /* for backwards compatibility, the following code emulates a fixed bug that
12283 occured when pushing elements (causing elements that just made their last
12284 pushing step to already (if possible) make their first falling step in the
12285 same game frame, which is bad); this code is also needed to use the famous
12286 "spring push bug" which is used in older levels and might be wanted to be
12287 used also in newer levels, but in this case the buggy pushing code is only
12288 affecting the "spring" element and no other elements */
12290 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12292 for (i = 0; i < MAX_PLAYERS; i++)
12294 struct PlayerInfo *player = &stored_player[i];
12295 int x = player->jx;
12296 int y = player->jy;
12298 if (player->active && player->is_pushing && player->is_moving &&
12300 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12301 Feld[x][y] == EL_SPRING))
12303 ContinueMoving(x, y);
12305 /* continue moving after pushing (this is actually a bug) */
12306 if (!IS_MOVING(x, y))
12307 Stop[x][y] = FALSE;
12313 debug_print_timestamp(0, "start main loop profiling");
12316 SCAN_PLAYFIELD(x, y)
12318 ChangeCount[x][y] = 0;
12319 ChangeEvent[x][y] = -1;
12321 /* this must be handled before main playfield loop */
12322 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12325 if (MovDelay[x][y] <= 0)
12329 #if USE_NEW_SNAP_DELAY
12330 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12333 if (MovDelay[x][y] <= 0)
12336 TEST_DrawLevelField(x, y);
12338 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12344 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12346 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12347 printf("GameActions(): This should never happen!\n");
12349 ChangePage[x][y] = -1;
12353 Stop[x][y] = FALSE;
12354 if (WasJustMoving[x][y] > 0)
12355 WasJustMoving[x][y]--;
12356 if (WasJustFalling[x][y] > 0)
12357 WasJustFalling[x][y]--;
12358 if (CheckCollision[x][y] > 0)
12359 CheckCollision[x][y]--;
12360 if (CheckImpact[x][y] > 0)
12361 CheckImpact[x][y]--;
12365 /* reset finished pushing action (not done in ContinueMoving() to allow
12366 continuous pushing animation for elements with zero push delay) */
12367 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12369 ResetGfxAnimation(x, y);
12370 TEST_DrawLevelField(x, y);
12374 if (IS_BLOCKED(x, y))
12378 Blocked2Moving(x, y, &oldx, &oldy);
12379 if (!IS_MOVING(oldx, oldy))
12381 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12382 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12383 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12384 printf("GameActions(): This should never happen!\n");
12391 debug_print_timestamp(0, "- time for pre-main loop:");
12394 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12395 SCAN_PLAYFIELD(x, y)
12397 element = Feld[x][y];
12398 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12403 int element2 = element;
12404 int graphic2 = graphic;
12406 int element2 = Feld[x][y];
12407 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12409 int last_gfx_frame = GfxFrame[x][y];
12411 if (graphic_info[graphic2].anim_global_sync)
12412 GfxFrame[x][y] = FrameCounter;
12413 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12414 GfxFrame[x][y] = CustomValue[x][y];
12415 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12416 GfxFrame[x][y] = element_info[element2].collect_score;
12417 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12418 GfxFrame[x][y] = ChangeDelay[x][y];
12420 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12421 DrawLevelGraphicAnimation(x, y, graphic2);
12424 ResetGfxFrame(x, y, TRUE);
12428 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12429 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12430 ResetRandomAnimationValue(x, y);
12434 SetRandomAnimationValue(x, y);
12438 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12441 #endif // -------------------- !!! TEST ONLY !!! --------------------
12444 debug_print_timestamp(0, "- time for TEST loop: -->");
12447 SCAN_PLAYFIELD(x, y)
12449 element = Feld[x][y];
12450 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12452 ResetGfxFrame(x, y, TRUE);
12454 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12455 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12456 ResetRandomAnimationValue(x, y);
12458 SetRandomAnimationValue(x, y);
12460 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12462 if (IS_INACTIVE(element))
12464 if (IS_ANIMATED(graphic))
12465 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12470 /* this may take place after moving, so 'element' may have changed */
12471 if (IS_CHANGING(x, y) &&
12472 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12474 int page = element_info[element].event_page_nr[CE_DELAY];
12477 HandleElementChange(x, y, page);
12479 if (CAN_CHANGE(element))
12480 HandleElementChange(x, y, page);
12482 if (HAS_ACTION(element))
12483 ExecuteCustomElementAction(x, y, element, page);
12486 element = Feld[x][y];
12487 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12490 #if 0 // ---------------------------------------------------------------------
12492 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12496 element = Feld[x][y];
12497 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12499 if (IS_ANIMATED(graphic) &&
12500 !IS_MOVING(x, y) &&
12502 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12504 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12505 TEST_DrawTwinkleOnField(x, y);
12507 else if (IS_MOVING(x, y))
12508 ContinueMoving(x, y);
12515 case EL_EM_EXIT_OPEN:
12516 case EL_SP_EXIT_OPEN:
12517 case EL_STEEL_EXIT_OPEN:
12518 case EL_EM_STEEL_EXIT_OPEN:
12519 case EL_SP_TERMINAL:
12520 case EL_SP_TERMINAL_ACTIVE:
12521 case EL_EXTRA_TIME:
12522 case EL_SHIELD_NORMAL:
12523 case EL_SHIELD_DEADLY:
12524 if (IS_ANIMATED(graphic))
12525 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12528 case EL_DYNAMITE_ACTIVE:
12529 case EL_EM_DYNAMITE_ACTIVE:
12530 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12531 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12532 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12533 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12534 case EL_SP_DISK_RED_ACTIVE:
12535 CheckDynamite(x, y);
12538 case EL_AMOEBA_GROWING:
12539 AmoebeWaechst(x, y);
12542 case EL_AMOEBA_SHRINKING:
12543 AmoebaDisappearing(x, y);
12546 #if !USE_NEW_AMOEBA_CODE
12547 case EL_AMOEBA_WET:
12548 case EL_AMOEBA_DRY:
12549 case EL_AMOEBA_FULL:
12551 case EL_EMC_DRIPPER:
12552 AmoebeAbleger(x, y);
12556 case EL_GAME_OF_LIFE:
12561 case EL_EXIT_CLOSED:
12565 case EL_EM_EXIT_CLOSED:
12569 case EL_STEEL_EXIT_CLOSED:
12570 CheckExitSteel(x, y);
12573 case EL_EM_STEEL_EXIT_CLOSED:
12574 CheckExitSteelEM(x, y);
12577 case EL_SP_EXIT_CLOSED:
12581 case EL_EXPANDABLE_WALL_GROWING:
12582 case EL_EXPANDABLE_STEELWALL_GROWING:
12583 MauerWaechst(x, y);
12586 case EL_EXPANDABLE_WALL:
12587 case EL_EXPANDABLE_WALL_HORIZONTAL:
12588 case EL_EXPANDABLE_WALL_VERTICAL:
12589 case EL_EXPANDABLE_WALL_ANY:
12590 case EL_BD_EXPANDABLE_WALL:
12591 MauerAbleger(x, y);
12594 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12595 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12596 case EL_EXPANDABLE_STEELWALL_ANY:
12597 MauerAblegerStahl(x, y);
12601 CheckForDragon(x, y);
12607 case EL_ELEMENT_SNAPPING:
12608 case EL_DIAGONAL_SHRINKING:
12609 case EL_DIAGONAL_GROWING:
12612 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12614 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12619 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12620 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12625 #else // ---------------------------------------------------------------------
12627 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12631 element = Feld[x][y];
12632 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12634 if (IS_ANIMATED(graphic) &&
12635 !IS_MOVING(x, y) &&
12637 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12639 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12640 TEST_DrawTwinkleOnField(x, y);
12642 else if ((element == EL_ACID ||
12643 element == EL_EXIT_OPEN ||
12644 element == EL_EM_EXIT_OPEN ||
12645 element == EL_SP_EXIT_OPEN ||
12646 element == EL_STEEL_EXIT_OPEN ||
12647 element == EL_EM_STEEL_EXIT_OPEN ||
12648 element == EL_SP_TERMINAL ||
12649 element == EL_SP_TERMINAL_ACTIVE ||
12650 element == EL_EXTRA_TIME ||
12651 element == EL_SHIELD_NORMAL ||
12652 element == EL_SHIELD_DEADLY) &&
12653 IS_ANIMATED(graphic))
12654 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12655 else if (IS_MOVING(x, y))
12656 ContinueMoving(x, y);
12657 else if (IS_ACTIVE_BOMB(element))
12658 CheckDynamite(x, y);
12659 else if (element == EL_AMOEBA_GROWING)
12660 AmoebeWaechst(x, y);
12661 else if (element == EL_AMOEBA_SHRINKING)
12662 AmoebaDisappearing(x, y);
12664 #if !USE_NEW_AMOEBA_CODE
12665 else if (IS_AMOEBALIVE(element))
12666 AmoebeAbleger(x, y);
12669 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12671 else if (element == EL_EXIT_CLOSED)
12673 else if (element == EL_EM_EXIT_CLOSED)
12675 else if (element == EL_STEEL_EXIT_CLOSED)
12676 CheckExitSteel(x, y);
12677 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12678 CheckExitSteelEM(x, y);
12679 else if (element == EL_SP_EXIT_CLOSED)
12681 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12682 element == EL_EXPANDABLE_STEELWALL_GROWING)
12683 MauerWaechst(x, y);
12684 else if (element == EL_EXPANDABLE_WALL ||
12685 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12686 element == EL_EXPANDABLE_WALL_VERTICAL ||
12687 element == EL_EXPANDABLE_WALL_ANY ||
12688 element == EL_BD_EXPANDABLE_WALL)
12689 MauerAbleger(x, y);
12690 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12691 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12692 element == EL_EXPANDABLE_STEELWALL_ANY)
12693 MauerAblegerStahl(x, y);
12694 else if (element == EL_FLAMES)
12695 CheckForDragon(x, y);
12696 else if (element == EL_EXPLOSION)
12697 ; /* drawing of correct explosion animation is handled separately */
12698 else if (element == EL_ELEMENT_SNAPPING ||
12699 element == EL_DIAGONAL_SHRINKING ||
12700 element == EL_DIAGONAL_GROWING)
12702 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12704 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12706 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12707 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12709 #endif // ---------------------------------------------------------------------
12711 if (IS_BELT_ACTIVE(element))
12712 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12714 if (game.magic_wall_active)
12716 int jx = local_player->jx, jy = local_player->jy;
12718 /* play the element sound at the position nearest to the player */
12719 if ((element == EL_MAGIC_WALL_FULL ||
12720 element == EL_MAGIC_WALL_ACTIVE ||
12721 element == EL_MAGIC_WALL_EMPTYING ||
12722 element == EL_BD_MAGIC_WALL_FULL ||
12723 element == EL_BD_MAGIC_WALL_ACTIVE ||
12724 element == EL_BD_MAGIC_WALL_EMPTYING ||
12725 element == EL_DC_MAGIC_WALL_FULL ||
12726 element == EL_DC_MAGIC_WALL_ACTIVE ||
12727 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12728 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12737 debug_print_timestamp(0, "- time for MAIN loop: -->");
12740 #if USE_NEW_AMOEBA_CODE
12741 /* new experimental amoeba growth stuff */
12742 if (!(FrameCounter % 8))
12744 static unsigned long random = 1684108901;
12746 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12748 x = RND(lev_fieldx);
12749 y = RND(lev_fieldy);
12750 element = Feld[x][y];
12752 if (!IS_PLAYER(x,y) &&
12753 (element == EL_EMPTY ||
12754 CAN_GROW_INTO(element) ||
12755 element == EL_QUICKSAND_EMPTY ||
12756 element == EL_QUICKSAND_FAST_EMPTY ||
12757 element == EL_ACID_SPLASH_LEFT ||
12758 element == EL_ACID_SPLASH_RIGHT))
12760 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12761 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12762 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12763 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12764 Feld[x][y] = EL_AMOEBA_DROP;
12767 random = random * 129 + 1;
12773 if (game.explosions_delayed)
12776 game.explosions_delayed = FALSE;
12778 SCAN_PLAYFIELD(x, y)
12780 element = Feld[x][y];
12782 if (ExplodeField[x][y])
12783 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12784 else if (element == EL_EXPLOSION)
12785 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12787 ExplodeField[x][y] = EX_TYPE_NONE;
12790 game.explosions_delayed = TRUE;
12793 if (game.magic_wall_active)
12795 if (!(game.magic_wall_time_left % 4))
12797 int element = Feld[magic_wall_x][magic_wall_y];
12799 if (element == EL_BD_MAGIC_WALL_FULL ||
12800 element == EL_BD_MAGIC_WALL_ACTIVE ||
12801 element == EL_BD_MAGIC_WALL_EMPTYING)
12802 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12803 else if (element == EL_DC_MAGIC_WALL_FULL ||
12804 element == EL_DC_MAGIC_WALL_ACTIVE ||
12805 element == EL_DC_MAGIC_WALL_EMPTYING)
12806 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12808 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12811 if (game.magic_wall_time_left > 0)
12813 game.magic_wall_time_left--;
12815 if (!game.magic_wall_time_left)
12817 SCAN_PLAYFIELD(x, y)
12819 element = Feld[x][y];
12821 if (element == EL_MAGIC_WALL_ACTIVE ||
12822 element == EL_MAGIC_WALL_FULL)
12824 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12825 TEST_DrawLevelField(x, y);
12827 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12828 element == EL_BD_MAGIC_WALL_FULL)
12830 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12831 TEST_DrawLevelField(x, y);
12833 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12834 element == EL_DC_MAGIC_WALL_FULL)
12836 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12837 TEST_DrawLevelField(x, y);
12841 game.magic_wall_active = FALSE;
12846 if (game.light_time_left > 0)
12848 game.light_time_left--;
12850 if (game.light_time_left == 0)
12851 RedrawAllLightSwitchesAndInvisibleElements();
12854 if (game.timegate_time_left > 0)
12856 game.timegate_time_left--;
12858 if (game.timegate_time_left == 0)
12859 CloseAllOpenTimegates();
12862 if (game.lenses_time_left > 0)
12864 game.lenses_time_left--;
12866 if (game.lenses_time_left == 0)
12867 RedrawAllInvisibleElementsForLenses();
12870 if (game.magnify_time_left > 0)
12872 game.magnify_time_left--;
12874 if (game.magnify_time_left == 0)
12875 RedrawAllInvisibleElementsForMagnifier();
12878 for (i = 0; i < MAX_PLAYERS; i++)
12880 struct PlayerInfo *player = &stored_player[i];
12882 if (SHIELD_ON(player))
12884 if (player->shield_deadly_time_left)
12885 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12886 else if (player->shield_normal_time_left)
12887 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12891 #if USE_DELAYED_GFX_REDRAW
12892 SCAN_PLAYFIELD(x, y)
12895 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12897 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
12898 GfxRedraw[x][y] != GFX_REDRAW_NONE)
12901 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12902 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12904 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12905 DrawLevelField(x, y);
12907 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12908 DrawLevelFieldCrumbledSand(x, y);
12910 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12911 DrawLevelFieldCrumbledSandNeighbours(x, y);
12913 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12914 DrawTwinkleOnField(x, y);
12917 GfxRedraw[x][y] = GFX_REDRAW_NONE;
12924 PlayAllPlayersSound();
12926 if (options.debug) /* calculate frames per second */
12928 static unsigned long fps_counter = 0;
12929 static int fps_frames = 0;
12930 unsigned long fps_delay_ms = Counter() - fps_counter;
12934 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12936 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12939 fps_counter = Counter();
12942 redraw_mask |= REDRAW_FPS;
12945 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12947 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12949 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12951 local_player->show_envelope = 0;
12955 debug_print_timestamp(0, "stop main loop profiling ");
12956 printf("----------------------------------------------------------\n");
12959 /* use random number generator in every frame to make it less predictable */
12960 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12964 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12966 int min_x = x, min_y = y, max_x = x, max_y = y;
12969 for (i = 0; i < MAX_PLAYERS; i++)
12971 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12973 if (!stored_player[i].active || &stored_player[i] == player)
12976 min_x = MIN(min_x, jx);
12977 min_y = MIN(min_y, jy);
12978 max_x = MAX(max_x, jx);
12979 max_y = MAX(max_y, jy);
12982 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12985 static boolean AllPlayersInVisibleScreen()
12989 for (i = 0; i < MAX_PLAYERS; i++)
12991 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12993 if (!stored_player[i].active)
12996 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13003 void ScrollLevel(int dx, int dy)
13006 /* (directly solved in BlitBitmap() now) */
13007 static Bitmap *bitmap_db_field2 = NULL;
13008 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13015 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13016 /* only horizontal XOR vertical scroll direction allowed */
13017 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13022 /* (directly solved in BlitBitmap() now) */
13023 if (bitmap_db_field2 == NULL)
13024 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13026 /* needed when blitting directly to same bitmap -- should not be needed with
13027 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13028 BlitBitmap(drawto_field, bitmap_db_field2,
13029 FX + TILEX * (dx == -1) - softscroll_offset,
13030 FY + TILEY * (dy == -1) - softscroll_offset,
13031 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13032 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13033 FX + TILEX * (dx == 1) - softscroll_offset,
13034 FY + TILEY * (dy == 1) - softscroll_offset);
13035 BlitBitmap(bitmap_db_field2, drawto_field,
13036 FX + TILEX * (dx == 1) - softscroll_offset,
13037 FY + TILEY * (dy == 1) - softscroll_offset,
13038 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13039 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13040 FX + TILEX * (dx == 1) - softscroll_offset,
13041 FY + TILEY * (dy == 1) - softscroll_offset);
13046 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13047 int xsize = (BX2 - BX1 + 1);
13048 int ysize = (BY2 - BY1 + 1);
13049 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13050 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13051 int step = (start < end ? +1 : -1);
13053 for (i = start; i != end; i += step)
13055 BlitBitmap(drawto_field, drawto_field,
13056 FX + TILEX * (dx != 0 ? i + step : 0),
13057 FY + TILEY * (dy != 0 ? i + step : 0),
13058 TILEX * (dx != 0 ? 1 : xsize),
13059 TILEY * (dy != 0 ? 1 : ysize),
13060 FX + TILEX * (dx != 0 ? i : 0),
13061 FY + TILEY * (dy != 0 ? i : 0));
13066 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13068 BlitBitmap(drawto_field, drawto_field,
13069 FX + TILEX * (dx == -1) - softscroll_offset,
13070 FY + TILEY * (dy == -1) - softscroll_offset,
13071 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13072 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13073 FX + TILEX * (dx == 1) - softscroll_offset,
13074 FY + TILEY * (dy == 1) - softscroll_offset);
13080 x = (dx == 1 ? BX1 : BX2);
13081 for (y = BY1; y <= BY2; y++)
13082 DrawScreenField(x, y);
13087 y = (dy == 1 ? BY1 : BY2);
13088 for (x = BX1; x <= BX2; x++)
13089 DrawScreenField(x, y);
13092 redraw_mask |= REDRAW_FIELD;
13095 static boolean canFallDown(struct PlayerInfo *player)
13097 int jx = player->jx, jy = player->jy;
13099 return (IN_LEV_FIELD(jx, jy + 1) &&
13100 (IS_FREE(jx, jy + 1) ||
13101 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13102 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13103 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13106 static boolean canPassField(int x, int y, int move_dir)
13108 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13109 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13110 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13111 int nextx = x + dx;
13112 int nexty = y + dy;
13113 int element = Feld[x][y];
13115 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13116 !CAN_MOVE(element) &&
13117 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13118 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13119 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13122 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13124 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13125 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13126 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13130 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13131 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13132 (IS_DIGGABLE(Feld[newx][newy]) ||
13133 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13134 canPassField(newx, newy, move_dir)));
13137 static void CheckGravityMovement(struct PlayerInfo *player)
13139 #if USE_PLAYER_GRAVITY
13140 if (player->gravity && !player->programmed_action)
13142 if (game.gravity && !player->programmed_action)
13145 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13146 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13147 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13148 int jx = player->jx, jy = player->jy;
13149 boolean player_is_moving_to_valid_field =
13150 (!player_is_snapping &&
13151 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13152 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13153 boolean player_can_fall_down = canFallDown(player);
13155 if (player_can_fall_down &&
13156 !player_is_moving_to_valid_field)
13157 player->programmed_action = MV_DOWN;
13161 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13163 return CheckGravityMovement(player);
13165 #if USE_PLAYER_GRAVITY
13166 if (player->gravity && !player->programmed_action)
13168 if (game.gravity && !player->programmed_action)
13171 int jx = player->jx, jy = player->jy;
13172 boolean field_under_player_is_free =
13173 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13174 boolean player_is_standing_on_valid_field =
13175 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13176 (IS_WALKABLE(Feld[jx][jy]) &&
13177 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13179 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13180 player->programmed_action = MV_DOWN;
13185 MovePlayerOneStep()
13186 -----------------------------------------------------------------------------
13187 dx, dy: direction (non-diagonal) to try to move the player to
13188 real_dx, real_dy: direction as read from input device (can be diagonal)
13191 boolean MovePlayerOneStep(struct PlayerInfo *player,
13192 int dx, int dy, int real_dx, int real_dy)
13194 int jx = player->jx, jy = player->jy;
13195 int new_jx = jx + dx, new_jy = jy + dy;
13196 #if !USE_FIXED_DONT_RUN_INTO
13200 boolean player_can_move = !player->cannot_move;
13202 if (!player->active || (!dx && !dy))
13203 return MP_NO_ACTION;
13205 player->MovDir = (dx < 0 ? MV_LEFT :
13206 dx > 0 ? MV_RIGHT :
13208 dy > 0 ? MV_DOWN : MV_NONE);
13210 if (!IN_LEV_FIELD(new_jx, new_jy))
13211 return MP_NO_ACTION;
13213 if (!player_can_move)
13215 if (player->MovPos == 0)
13217 player->is_moving = FALSE;
13218 player->is_digging = FALSE;
13219 player->is_collecting = FALSE;
13220 player->is_snapping = FALSE;
13221 player->is_pushing = FALSE;
13226 if (!options.network && game.centered_player_nr == -1 &&
13227 !AllPlayersInSight(player, new_jx, new_jy))
13228 return MP_NO_ACTION;
13230 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13231 return MP_NO_ACTION;
13234 #if !USE_FIXED_DONT_RUN_INTO
13235 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13237 /* (moved to DigField()) */
13238 if (player_can_move && DONT_RUN_INTO(element))
13240 if (element == EL_ACID && dx == 0 && dy == 1)
13242 SplashAcid(new_jx, new_jy);
13243 Feld[jx][jy] = EL_PLAYER_1;
13244 InitMovingField(jx, jy, MV_DOWN);
13245 Store[jx][jy] = EL_ACID;
13246 ContinueMoving(jx, jy);
13247 BuryPlayer(player);
13250 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13256 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13257 if (can_move != MP_MOVING)
13260 /* check if DigField() has caused relocation of the player */
13261 if (player->jx != jx || player->jy != jy)
13262 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13264 StorePlayer[jx][jy] = 0;
13265 player->last_jx = jx;
13266 player->last_jy = jy;
13267 player->jx = new_jx;
13268 player->jy = new_jy;
13269 StorePlayer[new_jx][new_jy] = player->element_nr;
13271 if (player->move_delay_value_next != -1)
13273 player->move_delay_value = player->move_delay_value_next;
13274 player->move_delay_value_next = -1;
13278 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13280 player->step_counter++;
13282 PlayerVisit[jx][jy] = FrameCounter;
13284 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13285 player->is_moving = TRUE;
13289 /* should better be called in MovePlayer(), but this breaks some tapes */
13290 ScrollPlayer(player, SCROLL_INIT);
13296 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13298 int jx = player->jx, jy = player->jy;
13299 int old_jx = jx, old_jy = jy;
13300 int moved = MP_NO_ACTION;
13302 if (!player->active)
13307 if (player->MovPos == 0)
13309 player->is_moving = FALSE;
13310 player->is_digging = FALSE;
13311 player->is_collecting = FALSE;
13312 player->is_snapping = FALSE;
13313 player->is_pushing = FALSE;
13319 if (player->move_delay > 0)
13322 player->move_delay = -1; /* set to "uninitialized" value */
13324 /* store if player is automatically moved to next field */
13325 player->is_auto_moving = (player->programmed_action != MV_NONE);
13327 /* remove the last programmed player action */
13328 player->programmed_action = 0;
13330 if (player->MovPos)
13332 /* should only happen if pre-1.2 tape recordings are played */
13333 /* this is only for backward compatibility */
13335 int original_move_delay_value = player->move_delay_value;
13338 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13342 /* scroll remaining steps with finest movement resolution */
13343 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13345 while (player->MovPos)
13347 ScrollPlayer(player, SCROLL_GO_ON);
13348 ScrollScreen(NULL, SCROLL_GO_ON);
13350 AdvanceFrameAndPlayerCounters(player->index_nr);
13356 player->move_delay_value = original_move_delay_value;
13359 player->is_active = FALSE;
13361 if (player->last_move_dir & MV_HORIZONTAL)
13363 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13364 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13368 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13369 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13372 #if USE_FIXED_BORDER_RUNNING_GFX
13373 if (!moved && !player->is_active)
13375 player->is_moving = FALSE;
13376 player->is_digging = FALSE;
13377 player->is_collecting = FALSE;
13378 player->is_snapping = FALSE;
13379 player->is_pushing = FALSE;
13387 if (moved & MP_MOVING && !ScreenMovPos &&
13388 (player->index_nr == game.centered_player_nr ||
13389 game.centered_player_nr == -1))
13391 if (moved & MP_MOVING && !ScreenMovPos &&
13392 (player == local_player || !options.network))
13395 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13396 int offset = game.scroll_delay_value;
13398 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13400 /* actual player has left the screen -- scroll in that direction */
13401 if (jx != old_jx) /* player has moved horizontally */
13402 scroll_x += (jx - old_jx);
13403 else /* player has moved vertically */
13404 scroll_y += (jy - old_jy);
13408 if (jx != old_jx) /* player has moved horizontally */
13410 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13411 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13412 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13414 /* don't scroll over playfield boundaries */
13415 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13416 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13418 /* don't scroll more than one field at a time */
13419 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13421 /* don't scroll against the player's moving direction */
13422 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13423 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13424 scroll_x = old_scroll_x;
13426 else /* player has moved vertically */
13428 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13429 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13430 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13432 /* don't scroll over playfield boundaries */
13433 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13434 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13436 /* don't scroll more than one field at a time */
13437 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13439 /* don't scroll against the player's moving direction */
13440 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13441 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13442 scroll_y = old_scroll_y;
13446 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13449 if (!options.network && game.centered_player_nr == -1 &&
13450 !AllPlayersInVisibleScreen())
13452 scroll_x = old_scroll_x;
13453 scroll_y = old_scroll_y;
13457 if (!options.network && !AllPlayersInVisibleScreen())
13459 scroll_x = old_scroll_x;
13460 scroll_y = old_scroll_y;
13465 ScrollScreen(player, SCROLL_INIT);
13466 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13471 player->StepFrame = 0;
13473 if (moved & MP_MOVING)
13475 if (old_jx != jx && old_jy == jy)
13476 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13477 else if (old_jx == jx && old_jy != jy)
13478 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13480 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13482 player->last_move_dir = player->MovDir;
13483 player->is_moving = TRUE;
13484 player->is_snapping = FALSE;
13485 player->is_switching = FALSE;
13486 player->is_dropping = FALSE;
13487 player->is_dropping_pressed = FALSE;
13488 player->drop_pressed_delay = 0;
13491 /* should better be called here than above, but this breaks some tapes */
13492 ScrollPlayer(player, SCROLL_INIT);
13497 CheckGravityMovementWhenNotMoving(player);
13499 player->is_moving = FALSE;
13501 /* at this point, the player is allowed to move, but cannot move right now
13502 (e.g. because of something blocking the way) -- ensure that the player
13503 is also allowed to move in the next frame (in old versions before 3.1.1,
13504 the player was forced to wait again for eight frames before next try) */
13506 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13507 player->move_delay = 0; /* allow direct movement in the next frame */
13510 if (player->move_delay == -1) /* not yet initialized by DigField() */
13511 player->move_delay = player->move_delay_value;
13513 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13515 TestIfPlayerTouchesBadThing(jx, jy);
13516 TestIfPlayerTouchesCustomElement(jx, jy);
13519 if (!player->active)
13520 RemovePlayer(player);
13525 void ScrollPlayer(struct PlayerInfo *player, int mode)
13527 int jx = player->jx, jy = player->jy;
13528 int last_jx = player->last_jx, last_jy = player->last_jy;
13529 int move_stepsize = TILEX / player->move_delay_value;
13531 #if USE_NEW_PLAYER_SPEED
13532 if (!player->active)
13535 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13538 if (!player->active || player->MovPos == 0)
13542 if (mode == SCROLL_INIT)
13544 player->actual_frame_counter = FrameCounter;
13545 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13547 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13548 Feld[last_jx][last_jy] == EL_EMPTY)
13550 int last_field_block_delay = 0; /* start with no blocking at all */
13551 int block_delay_adjustment = player->block_delay_adjustment;
13553 /* if player blocks last field, add delay for exactly one move */
13554 if (player->block_last_field)
13556 last_field_block_delay += player->move_delay_value;
13558 /* when blocking enabled, prevent moving up despite gravity */
13559 #if USE_PLAYER_GRAVITY
13560 if (player->gravity && player->MovDir == MV_UP)
13561 block_delay_adjustment = -1;
13563 if (game.gravity && player->MovDir == MV_UP)
13564 block_delay_adjustment = -1;
13568 /* add block delay adjustment (also possible when not blocking) */
13569 last_field_block_delay += block_delay_adjustment;
13571 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13572 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13575 #if USE_NEW_PLAYER_SPEED
13576 if (player->MovPos != 0) /* player has not yet reached destination */
13582 else if (!FrameReached(&player->actual_frame_counter, 1))
13585 #if USE_NEW_PLAYER_SPEED
13586 if (player->MovPos != 0)
13588 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13589 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13591 /* before DrawPlayer() to draw correct player graphic for this case */
13592 if (player->MovPos == 0)
13593 CheckGravityMovement(player);
13596 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13597 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13599 /* before DrawPlayer() to draw correct player graphic for this case */
13600 if (player->MovPos == 0)
13601 CheckGravityMovement(player);
13604 if (player->MovPos == 0) /* player reached destination field */
13606 if (player->move_delay_reset_counter > 0)
13608 player->move_delay_reset_counter--;
13610 if (player->move_delay_reset_counter == 0)
13612 /* continue with normal speed after quickly moving through gate */
13613 HALVE_PLAYER_SPEED(player);
13615 /* be able to make the next move without delay */
13616 player->move_delay = 0;
13620 player->last_jx = jx;
13621 player->last_jy = jy;
13623 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13624 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13625 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13626 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13627 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13628 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13630 DrawPlayer(player); /* needed here only to cleanup last field */
13631 RemovePlayer(player);
13633 if (local_player->friends_still_needed == 0 ||
13634 IS_SP_ELEMENT(Feld[jx][jy]))
13635 PlayerWins(player);
13638 /* this breaks one level: "machine", level 000 */
13640 int move_direction = player->MovDir;
13641 int enter_side = MV_DIR_OPPOSITE(move_direction);
13642 int leave_side = move_direction;
13643 int old_jx = last_jx;
13644 int old_jy = last_jy;
13645 int old_element = Feld[old_jx][old_jy];
13646 int new_element = Feld[jx][jy];
13648 if (IS_CUSTOM_ELEMENT(old_element))
13649 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13651 player->index_bit, leave_side);
13653 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13654 CE_PLAYER_LEAVES_X,
13655 player->index_bit, leave_side);
13657 if (IS_CUSTOM_ELEMENT(new_element))
13658 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13659 player->index_bit, enter_side);
13661 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13662 CE_PLAYER_ENTERS_X,
13663 player->index_bit, enter_side);
13665 #if USE_FIX_CE_ACTION_WITH_PLAYER
13666 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13667 CE_MOVE_OF_X, move_direction);
13669 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13670 CE_MOVE_OF_X, move_direction);
13674 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13676 TestIfPlayerTouchesBadThing(jx, jy);
13677 TestIfPlayerTouchesCustomElement(jx, jy);
13679 /* needed because pushed element has not yet reached its destination,
13680 so it would trigger a change event at its previous field location */
13681 if (!player->is_pushing)
13682 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13684 if (!player->active)
13685 RemovePlayer(player);
13688 if (!local_player->LevelSolved && level.use_step_counter)
13698 if (TimeLeft <= 10 && setup.time_limit)
13699 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13702 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13704 DisplayGameControlValues();
13706 DrawGameValue_Time(TimeLeft);
13709 if (!TimeLeft && setup.time_limit)
13710 for (i = 0; i < MAX_PLAYERS; i++)
13711 KillPlayer(&stored_player[i]);
13714 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13716 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13718 DisplayGameControlValues();
13721 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13722 DrawGameValue_Time(TimePlayed);
13726 if (tape.single_step && tape.recording && !tape.pausing &&
13727 !player->programmed_action)
13728 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13732 void ScrollScreen(struct PlayerInfo *player, int mode)
13734 static unsigned long screen_frame_counter = 0;
13736 if (mode == SCROLL_INIT)
13738 /* set scrolling step size according to actual player's moving speed */
13739 ScrollStepSize = TILEX / player->move_delay_value;
13741 screen_frame_counter = FrameCounter;
13742 ScreenMovDir = player->MovDir;
13743 ScreenMovPos = player->MovPos;
13744 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13747 else if (!FrameReached(&screen_frame_counter, 1))
13752 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13753 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13754 redraw_mask |= REDRAW_FIELD;
13757 ScreenMovDir = MV_NONE;
13760 void TestIfPlayerTouchesCustomElement(int x, int y)
13762 static int xy[4][2] =
13769 static int trigger_sides[4][2] =
13771 /* center side border side */
13772 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13773 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13774 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13775 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13777 static int touch_dir[4] =
13779 MV_LEFT | MV_RIGHT,
13784 int center_element = Feld[x][y]; /* should always be non-moving! */
13787 for (i = 0; i < NUM_DIRECTIONS; i++)
13789 int xx = x + xy[i][0];
13790 int yy = y + xy[i][1];
13791 int center_side = trigger_sides[i][0];
13792 int border_side = trigger_sides[i][1];
13793 int border_element;
13795 if (!IN_LEV_FIELD(xx, yy))
13798 if (IS_PLAYER(x, y)) /* player found at center element */
13800 struct PlayerInfo *player = PLAYERINFO(x, y);
13802 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13803 border_element = Feld[xx][yy]; /* may be moving! */
13804 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13805 border_element = Feld[xx][yy];
13806 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13807 border_element = MovingOrBlocked2Element(xx, yy);
13809 continue; /* center and border element do not touch */
13811 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13812 player->index_bit, border_side);
13813 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13814 CE_PLAYER_TOUCHES_X,
13815 player->index_bit, border_side);
13817 #if USE_FIX_CE_ACTION_WITH_PLAYER
13819 /* use player element that is initially defined in the level playfield,
13820 not the player element that corresponds to the runtime player number
13821 (example: a level that contains EL_PLAYER_3 as the only player would
13822 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13823 int player_element = PLAYERINFO(x, y)->initial_element;
13825 CheckElementChangeBySide(xx, yy, border_element, player_element,
13826 CE_TOUCHING_X, border_side);
13830 else if (IS_PLAYER(xx, yy)) /* player found at border element */
13832 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13834 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13836 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13837 continue; /* center and border element do not touch */
13840 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13841 player->index_bit, center_side);
13842 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13843 CE_PLAYER_TOUCHES_X,
13844 player->index_bit, center_side);
13846 #if USE_FIX_CE_ACTION_WITH_PLAYER
13848 /* use player element that is initially defined in the level playfield,
13849 not the player element that corresponds to the runtime player number
13850 (example: a level that contains EL_PLAYER_3 as the only player would
13851 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13852 int player_element = PLAYERINFO(xx, yy)->initial_element;
13854 CheckElementChangeBySide(x, y, center_element, player_element,
13855 CE_TOUCHING_X, center_side);
13864 #if USE_ELEMENT_TOUCHING_BUGFIX
13866 void TestIfElementTouchesCustomElement(int x, int y)
13868 static int xy[4][2] =
13875 static int trigger_sides[4][2] =
13877 /* center side border side */
13878 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13879 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13880 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13881 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13883 static int touch_dir[4] =
13885 MV_LEFT | MV_RIGHT,
13890 boolean change_center_element = FALSE;
13891 int center_element = Feld[x][y]; /* should always be non-moving! */
13892 int border_element_old[NUM_DIRECTIONS];
13895 for (i = 0; i < NUM_DIRECTIONS; i++)
13897 int xx = x + xy[i][0];
13898 int yy = y + xy[i][1];
13899 int border_element;
13901 border_element_old[i] = -1;
13903 if (!IN_LEV_FIELD(xx, yy))
13906 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13907 border_element = Feld[xx][yy]; /* may be moving! */
13908 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13909 border_element = Feld[xx][yy];
13910 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13911 border_element = MovingOrBlocked2Element(xx, yy);
13913 continue; /* center and border element do not touch */
13915 border_element_old[i] = border_element;
13918 for (i = 0; i < NUM_DIRECTIONS; i++)
13920 int xx = x + xy[i][0];
13921 int yy = y + xy[i][1];
13922 int center_side = trigger_sides[i][0];
13923 int border_element = border_element_old[i];
13925 if (border_element == -1)
13928 /* check for change of border element */
13929 CheckElementChangeBySide(xx, yy, border_element, center_element,
13930 CE_TOUCHING_X, center_side);
13932 /* (center element cannot be player, so we dont have to check this here) */
13935 for (i = 0; i < NUM_DIRECTIONS; i++)
13937 int xx = x + xy[i][0];
13938 int yy = y + xy[i][1];
13939 int border_side = trigger_sides[i][1];
13940 int border_element = border_element_old[i];
13942 if (border_element == -1)
13945 /* check for change of center element (but change it only once) */
13946 if (!change_center_element)
13947 change_center_element =
13948 CheckElementChangeBySide(x, y, center_element, border_element,
13949 CE_TOUCHING_X, border_side);
13951 #if USE_FIX_CE_ACTION_WITH_PLAYER
13952 if (IS_PLAYER(xx, yy))
13954 /* use player element that is initially defined in the level playfield,
13955 not the player element that corresponds to the runtime player number
13956 (example: a level that contains EL_PLAYER_3 as the only player would
13957 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13958 int player_element = PLAYERINFO(xx, yy)->initial_element;
13960 CheckElementChangeBySide(x, y, center_element, player_element,
13961 CE_TOUCHING_X, border_side);
13969 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13971 static int xy[4][2] =
13978 static int trigger_sides[4][2] =
13980 /* center side border side */
13981 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13982 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13983 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13984 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13986 static int touch_dir[4] =
13988 MV_LEFT | MV_RIGHT,
13993 boolean change_center_element = FALSE;
13994 int center_element = Feld[x][y]; /* should always be non-moving! */
13997 for (i = 0; i < NUM_DIRECTIONS; i++)
13999 int xx = x + xy[i][0];
14000 int yy = y + xy[i][1];
14001 int center_side = trigger_sides[i][0];
14002 int border_side = trigger_sides[i][1];
14003 int border_element;
14005 if (!IN_LEV_FIELD(xx, yy))
14008 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14009 border_element = Feld[xx][yy]; /* may be moving! */
14010 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14011 border_element = Feld[xx][yy];
14012 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14013 border_element = MovingOrBlocked2Element(xx, yy);
14015 continue; /* center and border element do not touch */
14017 /* check for change of center element (but change it only once) */
14018 if (!change_center_element)
14019 change_center_element =
14020 CheckElementChangeBySide(x, y, center_element, border_element,
14021 CE_TOUCHING_X, border_side);
14023 /* check for change of border element */
14024 CheckElementChangeBySide(xx, yy, border_element, center_element,
14025 CE_TOUCHING_X, center_side);
14031 void TestIfElementHitsCustomElement(int x, int y, int direction)
14033 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14034 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14035 int hitx = x + dx, hity = y + dy;
14036 int hitting_element = Feld[x][y];
14037 int touched_element;
14039 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14042 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14043 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14045 if (IN_LEV_FIELD(hitx, hity))
14047 int opposite_direction = MV_DIR_OPPOSITE(direction);
14048 int hitting_side = direction;
14049 int touched_side = opposite_direction;
14050 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14051 MovDir[hitx][hity] != direction ||
14052 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14058 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14059 CE_HITTING_X, touched_side);
14061 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14062 CE_HIT_BY_X, hitting_side);
14064 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14065 CE_HIT_BY_SOMETHING, opposite_direction);
14067 #if USE_FIX_CE_ACTION_WITH_PLAYER
14068 if (IS_PLAYER(hitx, hity))
14070 /* use player element that is initially defined in the level playfield,
14071 not the player element that corresponds to the runtime player number
14072 (example: a level that contains EL_PLAYER_3 as the only player would
14073 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14074 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14076 CheckElementChangeBySide(x, y, hitting_element, player_element,
14077 CE_HITTING_X, touched_side);
14083 /* "hitting something" is also true when hitting the playfield border */
14084 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14085 CE_HITTING_SOMETHING, direction);
14089 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14091 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14092 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14093 int hitx = x + dx, hity = y + dy;
14094 int hitting_element = Feld[x][y];
14095 int touched_element;
14097 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14098 !IS_FREE(hitx, hity) &&
14099 (!IS_MOVING(hitx, hity) ||
14100 MovDir[hitx][hity] != direction ||
14101 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14104 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14108 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14112 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14113 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14115 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14116 EP_CAN_SMASH_EVERYTHING, direction);
14118 if (IN_LEV_FIELD(hitx, hity))
14120 int opposite_direction = MV_DIR_OPPOSITE(direction);
14121 int hitting_side = direction;
14122 int touched_side = opposite_direction;
14124 int touched_element = MovingOrBlocked2Element(hitx, hity);
14127 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14128 MovDir[hitx][hity] != direction ||
14129 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14138 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14139 CE_SMASHED_BY_SOMETHING, opposite_direction);
14141 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14142 CE_OTHER_IS_SMASHING, touched_side);
14144 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14145 CE_OTHER_GETS_SMASHED, hitting_side);
14151 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14153 int i, kill_x = -1, kill_y = -1;
14155 int bad_element = -1;
14156 static int test_xy[4][2] =
14163 static int test_dir[4] =
14171 for (i = 0; i < NUM_DIRECTIONS; i++)
14173 int test_x, test_y, test_move_dir, test_element;
14175 test_x = good_x + test_xy[i][0];
14176 test_y = good_y + test_xy[i][1];
14178 if (!IN_LEV_FIELD(test_x, test_y))
14182 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14184 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14186 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14187 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14189 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14190 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14194 bad_element = test_element;
14200 if (kill_x != -1 || kill_y != -1)
14202 if (IS_PLAYER(good_x, good_y))
14204 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14206 if (player->shield_deadly_time_left > 0 &&
14207 !IS_INDESTRUCTIBLE(bad_element))
14208 Bang(kill_x, kill_y);
14209 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14210 KillPlayer(player);
14213 Bang(good_x, good_y);
14217 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14219 int i, kill_x = -1, kill_y = -1;
14220 int bad_element = Feld[bad_x][bad_y];
14221 static int test_xy[4][2] =
14228 static int touch_dir[4] =
14230 MV_LEFT | MV_RIGHT,
14235 static int test_dir[4] =
14243 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14246 for (i = 0; i < NUM_DIRECTIONS; i++)
14248 int test_x, test_y, test_move_dir, test_element;
14250 test_x = bad_x + test_xy[i][0];
14251 test_y = bad_y + test_xy[i][1];
14253 if (!IN_LEV_FIELD(test_x, test_y))
14257 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14259 test_element = Feld[test_x][test_y];
14261 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14262 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14264 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14265 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14267 /* good thing is player or penguin that does not move away */
14268 if (IS_PLAYER(test_x, test_y))
14270 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14272 if (bad_element == EL_ROBOT && player->is_moving)
14273 continue; /* robot does not kill player if he is moving */
14275 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14277 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14278 continue; /* center and border element do not touch */
14286 else if (test_element == EL_PENGUIN)
14296 if (kill_x != -1 || kill_y != -1)
14298 if (IS_PLAYER(kill_x, kill_y))
14300 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14302 if (player->shield_deadly_time_left > 0 &&
14303 !IS_INDESTRUCTIBLE(bad_element))
14304 Bang(bad_x, bad_y);
14305 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14306 KillPlayer(player);
14309 Bang(kill_x, kill_y);
14313 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14315 int bad_element = Feld[bad_x][bad_y];
14316 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14317 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14318 int test_x = bad_x + dx, test_y = bad_y + dy;
14319 int test_move_dir, test_element;
14320 int kill_x = -1, kill_y = -1;
14322 if (!IN_LEV_FIELD(test_x, test_y))
14326 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14328 test_element = Feld[test_x][test_y];
14330 if (test_move_dir != bad_move_dir)
14332 /* good thing can be player or penguin that does not move away */
14333 if (IS_PLAYER(test_x, test_y))
14335 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14337 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14338 player as being hit when he is moving towards the bad thing, because
14339 the "get hit by" condition would be lost after the player stops) */
14340 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14341 return; /* player moves away from bad thing */
14346 else if (test_element == EL_PENGUIN)
14353 if (kill_x != -1 || kill_y != -1)
14355 if (IS_PLAYER(kill_x, kill_y))
14357 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14359 if (player->shield_deadly_time_left > 0 &&
14360 !IS_INDESTRUCTIBLE(bad_element))
14361 Bang(bad_x, bad_y);
14362 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14363 KillPlayer(player);
14366 Bang(kill_x, kill_y);
14370 void TestIfPlayerTouchesBadThing(int x, int y)
14372 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14375 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14377 TestIfGoodThingHitsBadThing(x, y, move_dir);
14380 void TestIfBadThingTouchesPlayer(int x, int y)
14382 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14385 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14387 TestIfBadThingHitsGoodThing(x, y, move_dir);
14390 void TestIfFriendTouchesBadThing(int x, int y)
14392 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14395 void TestIfBadThingTouchesFriend(int x, int y)
14397 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14400 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14402 int i, kill_x = bad_x, kill_y = bad_y;
14403 static int xy[4][2] =
14411 for (i = 0; i < NUM_DIRECTIONS; i++)
14415 x = bad_x + xy[i][0];
14416 y = bad_y + xy[i][1];
14417 if (!IN_LEV_FIELD(x, y))
14420 element = Feld[x][y];
14421 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14422 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14430 if (kill_x != bad_x || kill_y != bad_y)
14431 Bang(bad_x, bad_y);
14434 void KillPlayer(struct PlayerInfo *player)
14436 int jx = player->jx, jy = player->jy;
14438 if (!player->active)
14442 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14443 player->killed, player->active, player->reanimated);
14446 /* the following code was introduced to prevent an infinite loop when calling
14448 -> CheckTriggeredElementChangeExt()
14449 -> ExecuteCustomElementAction()
14451 -> (infinitely repeating the above sequence of function calls)
14452 which occurs when killing the player while having a CE with the setting
14453 "kill player X when explosion of <player X>"; the solution using a new
14454 field "player->killed" was chosen for backwards compatibility, although
14455 clever use of the fields "player->active" etc. would probably also work */
14457 if (player->killed)
14461 player->killed = TRUE;
14463 /* remove accessible field at the player's position */
14464 Feld[jx][jy] = EL_EMPTY;
14466 /* deactivate shield (else Bang()/Explode() would not work right) */
14467 player->shield_normal_time_left = 0;
14468 player->shield_deadly_time_left = 0;
14471 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14472 player->killed, player->active, player->reanimated);
14478 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14479 player->killed, player->active, player->reanimated);
14482 #if USE_PLAYER_REANIMATION
14484 if (player->reanimated) /* killed player may have been reanimated */
14485 player->killed = player->reanimated = FALSE;
14487 BuryPlayer(player);
14489 if (player->killed) /* player may have been reanimated */
14490 BuryPlayer(player);
14493 BuryPlayer(player);
14497 static void KillPlayerUnlessEnemyProtected(int x, int y)
14499 if (!PLAYER_ENEMY_PROTECTED(x, y))
14500 KillPlayer(PLAYERINFO(x, y));
14503 static void KillPlayerUnlessExplosionProtected(int x, int y)
14505 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14506 KillPlayer(PLAYERINFO(x, y));
14509 void BuryPlayer(struct PlayerInfo *player)
14511 int jx = player->jx, jy = player->jy;
14513 if (!player->active)
14516 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14517 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14519 player->GameOver = TRUE;
14520 RemovePlayer(player);
14523 void RemovePlayer(struct PlayerInfo *player)
14525 int jx = player->jx, jy = player->jy;
14526 int i, found = FALSE;
14528 player->present = FALSE;
14529 player->active = FALSE;
14531 if (!ExplodeField[jx][jy])
14532 StorePlayer[jx][jy] = 0;
14534 if (player->is_moving)
14535 TEST_DrawLevelField(player->last_jx, player->last_jy);
14537 for (i = 0; i < MAX_PLAYERS; i++)
14538 if (stored_player[i].active)
14542 AllPlayersGone = TRUE;
14548 #if USE_NEW_SNAP_DELAY
14549 static void setFieldForSnapping(int x, int y, int element, int direction)
14551 struct ElementInfo *ei = &element_info[element];
14552 int direction_bit = MV_DIR_TO_BIT(direction);
14553 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14554 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14555 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14557 Feld[x][y] = EL_ELEMENT_SNAPPING;
14558 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14560 ResetGfxAnimation(x, y);
14562 GfxElement[x][y] = element;
14563 GfxAction[x][y] = action;
14564 GfxDir[x][y] = direction;
14565 GfxFrame[x][y] = -1;
14570 =============================================================================
14571 checkDiagonalPushing()
14572 -----------------------------------------------------------------------------
14573 check if diagonal input device direction results in pushing of object
14574 (by checking if the alternative direction is walkable, diggable, ...)
14575 =============================================================================
14578 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14579 int x, int y, int real_dx, int real_dy)
14581 int jx, jy, dx, dy, xx, yy;
14583 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14586 /* diagonal direction: check alternative direction */
14591 xx = jx + (dx == 0 ? real_dx : 0);
14592 yy = jy + (dy == 0 ? real_dy : 0);
14594 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14598 =============================================================================
14600 -----------------------------------------------------------------------------
14601 x, y: field next to player (non-diagonal) to try to dig to
14602 real_dx, real_dy: direction as read from input device (can be diagonal)
14603 =============================================================================
14606 static int DigField(struct PlayerInfo *player,
14607 int oldx, int oldy, int x, int y,
14608 int real_dx, int real_dy, int mode)
14610 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14611 boolean player_was_pushing = player->is_pushing;
14612 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14613 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14614 int jx = oldx, jy = oldy;
14615 int dx = x - jx, dy = y - jy;
14616 int nextx = x + dx, nexty = y + dy;
14617 int move_direction = (dx == -1 ? MV_LEFT :
14618 dx == +1 ? MV_RIGHT :
14620 dy == +1 ? MV_DOWN : MV_NONE);
14621 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14622 int dig_side = MV_DIR_OPPOSITE(move_direction);
14623 int old_element = Feld[jx][jy];
14624 #if USE_FIXED_DONT_RUN_INTO
14625 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14631 if (is_player) /* function can also be called by EL_PENGUIN */
14633 if (player->MovPos == 0)
14635 player->is_digging = FALSE;
14636 player->is_collecting = FALSE;
14639 if (player->MovPos == 0) /* last pushing move finished */
14640 player->is_pushing = FALSE;
14642 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14644 player->is_switching = FALSE;
14645 player->push_delay = -1;
14647 return MP_NO_ACTION;
14651 #if !USE_FIXED_DONT_RUN_INTO
14652 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14653 return MP_NO_ACTION;
14656 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14657 old_element = Back[jx][jy];
14659 /* in case of element dropped at player position, check background */
14660 else if (Back[jx][jy] != EL_EMPTY &&
14661 game.engine_version >= VERSION_IDENT(2,2,0,0))
14662 old_element = Back[jx][jy];
14664 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14665 return MP_NO_ACTION; /* field has no opening in this direction */
14667 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14668 return MP_NO_ACTION; /* field has no opening in this direction */
14670 #if USE_FIXED_DONT_RUN_INTO
14671 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14675 Feld[jx][jy] = player->artwork_element;
14676 InitMovingField(jx, jy, MV_DOWN);
14677 Store[jx][jy] = EL_ACID;
14678 ContinueMoving(jx, jy);
14679 BuryPlayer(player);
14681 return MP_DONT_RUN_INTO;
14685 #if USE_FIXED_DONT_RUN_INTO
14686 if (player_can_move && DONT_RUN_INTO(element))
14688 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14690 return MP_DONT_RUN_INTO;
14694 #if USE_FIXED_DONT_RUN_INTO
14695 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14696 return MP_NO_ACTION;
14699 #if !USE_FIXED_DONT_RUN_INTO
14700 element = Feld[x][y];
14703 collect_count = element_info[element].collect_count_initial;
14705 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14706 return MP_NO_ACTION;
14708 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14709 player_can_move = player_can_move_or_snap;
14711 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14712 game.engine_version >= VERSION_IDENT(2,2,0,0))
14714 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14715 player->index_bit, dig_side);
14716 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14717 player->index_bit, dig_side);
14719 if (element == EL_DC_LANDMINE)
14722 if (Feld[x][y] != element) /* field changed by snapping */
14725 return MP_NO_ACTION;
14728 #if USE_PLAYER_GRAVITY
14729 if (player->gravity && is_player && !player->is_auto_moving &&
14730 canFallDown(player) && move_direction != MV_DOWN &&
14731 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14732 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14734 if (game.gravity && is_player && !player->is_auto_moving &&
14735 canFallDown(player) && move_direction != MV_DOWN &&
14736 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14737 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14740 if (player_can_move &&
14741 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14743 int sound_element = SND_ELEMENT(element);
14744 int sound_action = ACTION_WALKING;
14746 if (IS_RND_GATE(element))
14748 if (!player->key[RND_GATE_NR(element)])
14749 return MP_NO_ACTION;
14751 else if (IS_RND_GATE_GRAY(element))
14753 if (!player->key[RND_GATE_GRAY_NR(element)])
14754 return MP_NO_ACTION;
14756 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14758 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14759 return MP_NO_ACTION;
14761 else if (element == EL_EXIT_OPEN ||
14762 element == EL_EM_EXIT_OPEN ||
14763 element == EL_STEEL_EXIT_OPEN ||
14764 element == EL_EM_STEEL_EXIT_OPEN ||
14765 element == EL_SP_EXIT_OPEN ||
14766 element == EL_SP_EXIT_OPENING)
14768 sound_action = ACTION_PASSING; /* player is passing exit */
14770 else if (element == EL_EMPTY)
14772 sound_action = ACTION_MOVING; /* nothing to walk on */
14775 /* play sound from background or player, whatever is available */
14776 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14777 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14779 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14781 else if (player_can_move &&
14782 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14784 if (!ACCESS_FROM(element, opposite_direction))
14785 return MP_NO_ACTION; /* field not accessible from this direction */
14787 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14788 return MP_NO_ACTION;
14790 if (IS_EM_GATE(element))
14792 if (!player->key[EM_GATE_NR(element)])
14793 return MP_NO_ACTION;
14795 else if (IS_EM_GATE_GRAY(element))
14797 if (!player->key[EM_GATE_GRAY_NR(element)])
14798 return MP_NO_ACTION;
14800 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14802 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14803 return MP_NO_ACTION;
14805 else if (IS_EMC_GATE(element))
14807 if (!player->key[EMC_GATE_NR(element)])
14808 return MP_NO_ACTION;
14810 else if (IS_EMC_GATE_GRAY(element))
14812 if (!player->key[EMC_GATE_GRAY_NR(element)])
14813 return MP_NO_ACTION;
14815 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14817 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14818 return MP_NO_ACTION;
14820 else if (element == EL_DC_GATE_WHITE ||
14821 element == EL_DC_GATE_WHITE_GRAY ||
14822 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14824 if (player->num_white_keys == 0)
14825 return MP_NO_ACTION;
14827 player->num_white_keys--;
14829 else if (IS_SP_PORT(element))
14831 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14832 element == EL_SP_GRAVITY_PORT_RIGHT ||
14833 element == EL_SP_GRAVITY_PORT_UP ||
14834 element == EL_SP_GRAVITY_PORT_DOWN)
14835 #if USE_PLAYER_GRAVITY
14836 player->gravity = !player->gravity;
14838 game.gravity = !game.gravity;
14840 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14841 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14842 element == EL_SP_GRAVITY_ON_PORT_UP ||
14843 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14844 #if USE_PLAYER_GRAVITY
14845 player->gravity = TRUE;
14847 game.gravity = TRUE;
14849 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14850 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14851 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14852 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14853 #if USE_PLAYER_GRAVITY
14854 player->gravity = FALSE;
14856 game.gravity = FALSE;
14860 /* automatically move to the next field with double speed */
14861 player->programmed_action = move_direction;
14863 if (player->move_delay_reset_counter == 0)
14865 player->move_delay_reset_counter = 2; /* two double speed steps */
14867 DOUBLE_PLAYER_SPEED(player);
14870 PlayLevelSoundAction(x, y, ACTION_PASSING);
14872 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14876 if (mode != DF_SNAP)
14878 GfxElement[x][y] = GFX_ELEMENT(element);
14879 player->is_digging = TRUE;
14882 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14884 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14885 player->index_bit, dig_side);
14887 if (mode == DF_SNAP)
14889 #if USE_NEW_SNAP_DELAY
14890 if (level.block_snap_field)
14891 setFieldForSnapping(x, y, element, move_direction);
14893 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14895 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14898 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14899 player->index_bit, dig_side);
14902 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14906 if (is_player && mode != DF_SNAP)
14908 GfxElement[x][y] = element;
14909 player->is_collecting = TRUE;
14912 if (element == EL_SPEED_PILL)
14914 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14916 else if (element == EL_EXTRA_TIME && level.time > 0)
14918 TimeLeft += level.extra_time;
14921 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14923 DisplayGameControlValues();
14925 DrawGameValue_Time(TimeLeft);
14928 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14930 player->shield_normal_time_left += level.shield_normal_time;
14931 if (element == EL_SHIELD_DEADLY)
14932 player->shield_deadly_time_left += level.shield_deadly_time;
14934 else if (element == EL_DYNAMITE ||
14935 element == EL_EM_DYNAMITE ||
14936 element == EL_SP_DISK_RED)
14938 if (player->inventory_size < MAX_INVENTORY_SIZE)
14939 player->inventory_element[player->inventory_size++] = element;
14941 DrawGameDoorValues();
14943 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14945 player->dynabomb_count++;
14946 player->dynabombs_left++;
14948 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14950 player->dynabomb_size++;
14952 else if (element == EL_DYNABOMB_INCREASE_POWER)
14954 player->dynabomb_xl = TRUE;
14956 else if (IS_KEY(element))
14958 player->key[KEY_NR(element)] = TRUE;
14960 DrawGameDoorValues();
14962 else if (element == EL_DC_KEY_WHITE)
14964 player->num_white_keys++;
14966 /* display white keys? */
14967 /* DrawGameDoorValues(); */
14969 else if (IS_ENVELOPE(element))
14971 player->show_envelope = element;
14973 else if (element == EL_EMC_LENSES)
14975 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14977 RedrawAllInvisibleElementsForLenses();
14979 else if (element == EL_EMC_MAGNIFIER)
14981 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14983 RedrawAllInvisibleElementsForMagnifier();
14985 else if (IS_DROPPABLE(element) ||
14986 IS_THROWABLE(element)) /* can be collected and dropped */
14990 if (collect_count == 0)
14991 player->inventory_infinite_element = element;
14993 for (i = 0; i < collect_count; i++)
14994 if (player->inventory_size < MAX_INVENTORY_SIZE)
14995 player->inventory_element[player->inventory_size++] = element;
14997 DrawGameDoorValues();
14999 else if (collect_count > 0)
15001 local_player->gems_still_needed -= collect_count;
15002 if (local_player->gems_still_needed < 0)
15003 local_player->gems_still_needed = 0;
15006 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15008 DisplayGameControlValues();
15010 DrawGameValue_Emeralds(local_player->gems_still_needed);
15014 RaiseScoreElement(element);
15015 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15018 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15019 player->index_bit, dig_side);
15021 if (mode == DF_SNAP)
15023 #if USE_NEW_SNAP_DELAY
15024 if (level.block_snap_field)
15025 setFieldForSnapping(x, y, element, move_direction);
15027 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15029 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15032 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15033 player->index_bit, dig_side);
15036 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15038 if (mode == DF_SNAP && element != EL_BD_ROCK)
15039 return MP_NO_ACTION;
15041 if (CAN_FALL(element) && dy)
15042 return MP_NO_ACTION;
15044 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15045 !(element == EL_SPRING && level.use_spring_bug))
15046 return MP_NO_ACTION;
15048 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15049 ((move_direction & MV_VERTICAL &&
15050 ((element_info[element].move_pattern & MV_LEFT &&
15051 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15052 (element_info[element].move_pattern & MV_RIGHT &&
15053 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15054 (move_direction & MV_HORIZONTAL &&
15055 ((element_info[element].move_pattern & MV_UP &&
15056 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15057 (element_info[element].move_pattern & MV_DOWN &&
15058 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15059 return MP_NO_ACTION;
15061 /* do not push elements already moving away faster than player */
15062 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15063 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15064 return MP_NO_ACTION;
15066 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15068 if (player->push_delay_value == -1 || !player_was_pushing)
15069 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15071 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15073 if (player->push_delay_value == -1)
15074 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15076 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15078 if (!player->is_pushing)
15079 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15082 player->is_pushing = TRUE;
15083 player->is_active = TRUE;
15085 if (!(IN_LEV_FIELD(nextx, nexty) &&
15086 (IS_FREE(nextx, nexty) ||
15087 (IS_SB_ELEMENT(element) &&
15088 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15089 (IS_CUSTOM_ELEMENT(element) &&
15090 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15091 return MP_NO_ACTION;
15093 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15094 return MP_NO_ACTION;
15096 if (player->push_delay == -1) /* new pushing; restart delay */
15097 player->push_delay = 0;
15099 if (player->push_delay < player->push_delay_value &&
15100 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15101 element != EL_SPRING && element != EL_BALLOON)
15103 /* make sure that there is no move delay before next try to push */
15104 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15105 player->move_delay = 0;
15107 return MP_NO_ACTION;
15110 if (IS_CUSTOM_ELEMENT(element) &&
15111 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15113 if (!DigFieldByCE(nextx, nexty, element))
15114 return MP_NO_ACTION;
15117 if (IS_SB_ELEMENT(element))
15119 if (element == EL_SOKOBAN_FIELD_FULL)
15121 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15122 local_player->sokobanfields_still_needed++;
15125 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15127 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15128 local_player->sokobanfields_still_needed--;
15131 Feld[x][y] = EL_SOKOBAN_OBJECT;
15133 if (Back[x][y] == Back[nextx][nexty])
15134 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15135 else if (Back[x][y] != 0)
15136 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15139 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15142 if (local_player->sokobanfields_still_needed == 0 &&
15143 game.emulation == EMU_SOKOBAN)
15145 PlayerWins(player);
15147 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15151 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15153 InitMovingField(x, y, move_direction);
15154 GfxAction[x][y] = ACTION_PUSHING;
15156 if (mode == DF_SNAP)
15157 ContinueMoving(x, y);
15159 MovPos[x][y] = (dx != 0 ? dx : dy);
15161 Pushed[x][y] = TRUE;
15162 Pushed[nextx][nexty] = TRUE;
15164 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15165 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15167 player->push_delay_value = -1; /* get new value later */
15169 /* check for element change _after_ element has been pushed */
15170 if (game.use_change_when_pushing_bug)
15172 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15173 player->index_bit, dig_side);
15174 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15175 player->index_bit, dig_side);
15178 else if (IS_SWITCHABLE(element))
15180 if (PLAYER_SWITCHING(player, x, y))
15182 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15183 player->index_bit, dig_side);
15188 player->is_switching = TRUE;
15189 player->switch_x = x;
15190 player->switch_y = y;
15192 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15194 if (element == EL_ROBOT_WHEEL)
15196 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15200 game.robot_wheel_active = TRUE;
15202 TEST_DrawLevelField(x, y);
15204 else if (element == EL_SP_TERMINAL)
15208 SCAN_PLAYFIELD(xx, yy)
15210 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15212 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15213 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15216 else if (IS_BELT_SWITCH(element))
15218 ToggleBeltSwitch(x, y);
15220 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15221 element == EL_SWITCHGATE_SWITCH_DOWN ||
15222 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15223 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15225 ToggleSwitchgateSwitch(x, y);
15227 else if (element == EL_LIGHT_SWITCH ||
15228 element == EL_LIGHT_SWITCH_ACTIVE)
15230 ToggleLightSwitch(x, y);
15232 else if (element == EL_TIMEGATE_SWITCH ||
15233 element == EL_DC_TIMEGATE_SWITCH)
15235 ActivateTimegateSwitch(x, y);
15237 else if (element == EL_BALLOON_SWITCH_LEFT ||
15238 element == EL_BALLOON_SWITCH_RIGHT ||
15239 element == EL_BALLOON_SWITCH_UP ||
15240 element == EL_BALLOON_SWITCH_DOWN ||
15241 element == EL_BALLOON_SWITCH_NONE ||
15242 element == EL_BALLOON_SWITCH_ANY)
15244 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15245 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15246 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15247 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15248 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15251 else if (element == EL_LAMP)
15253 Feld[x][y] = EL_LAMP_ACTIVE;
15254 local_player->lights_still_needed--;
15256 ResetGfxAnimation(x, y);
15257 TEST_DrawLevelField(x, y);
15259 else if (element == EL_TIME_ORB_FULL)
15261 Feld[x][y] = EL_TIME_ORB_EMPTY;
15263 if (level.time > 0 || level.use_time_orb_bug)
15265 TimeLeft += level.time_orb_time;
15268 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15270 DisplayGameControlValues();
15272 DrawGameValue_Time(TimeLeft);
15276 ResetGfxAnimation(x, y);
15277 TEST_DrawLevelField(x, y);
15279 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15280 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15284 game.ball_state = !game.ball_state;
15286 SCAN_PLAYFIELD(xx, yy)
15288 int e = Feld[xx][yy];
15290 if (game.ball_state)
15292 if (e == EL_EMC_MAGIC_BALL)
15293 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15294 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15295 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15299 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15300 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15301 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15302 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15307 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15308 player->index_bit, dig_side);
15310 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15311 player->index_bit, dig_side);
15313 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15314 player->index_bit, dig_side);
15320 if (!PLAYER_SWITCHING(player, x, y))
15322 player->is_switching = TRUE;
15323 player->switch_x = x;
15324 player->switch_y = y;
15326 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15327 player->index_bit, dig_side);
15328 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15329 player->index_bit, dig_side);
15331 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15332 player->index_bit, dig_side);
15333 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15334 player->index_bit, dig_side);
15337 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15338 player->index_bit, dig_side);
15339 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15340 player->index_bit, dig_side);
15342 return MP_NO_ACTION;
15345 player->push_delay = -1;
15347 if (is_player) /* function can also be called by EL_PENGUIN */
15349 if (Feld[x][y] != element) /* really digged/collected something */
15351 player->is_collecting = !player->is_digging;
15352 player->is_active = TRUE;
15359 static boolean DigFieldByCE(int x, int y, int digging_element)
15361 int element = Feld[x][y];
15363 if (!IS_FREE(x, y))
15365 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15366 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15369 /* no element can dig solid indestructible elements */
15370 if (IS_INDESTRUCTIBLE(element) &&
15371 !IS_DIGGABLE(element) &&
15372 !IS_COLLECTIBLE(element))
15375 if (AmoebaNr[x][y] &&
15376 (element == EL_AMOEBA_FULL ||
15377 element == EL_BD_AMOEBA ||
15378 element == EL_AMOEBA_GROWING))
15380 AmoebaCnt[AmoebaNr[x][y]]--;
15381 AmoebaCnt2[AmoebaNr[x][y]]--;
15384 if (IS_MOVING(x, y))
15385 RemoveMovingField(x, y);
15389 TEST_DrawLevelField(x, y);
15392 /* if digged element was about to explode, prevent the explosion */
15393 ExplodeField[x][y] = EX_TYPE_NONE;
15395 PlayLevelSoundAction(x, y, action);
15398 Store[x][y] = EL_EMPTY;
15401 /* this makes it possible to leave the removed element again */
15402 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15403 Store[x][y] = element;
15405 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15407 int move_leave_element = element_info[digging_element].move_leave_element;
15409 /* this makes it possible to leave the removed element again */
15410 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15411 element : move_leave_element);
15418 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15420 int jx = player->jx, jy = player->jy;
15421 int x = jx + dx, y = jy + dy;
15422 int snap_direction = (dx == -1 ? MV_LEFT :
15423 dx == +1 ? MV_RIGHT :
15425 dy == +1 ? MV_DOWN : MV_NONE);
15426 boolean can_continue_snapping = (level.continuous_snapping &&
15427 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15429 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15432 if (!player->active || !IN_LEV_FIELD(x, y))
15440 if (player->MovPos == 0)
15441 player->is_pushing = FALSE;
15443 player->is_snapping = FALSE;
15445 if (player->MovPos == 0)
15447 player->is_moving = FALSE;
15448 player->is_digging = FALSE;
15449 player->is_collecting = FALSE;
15455 #if USE_NEW_CONTINUOUS_SNAPPING
15456 /* prevent snapping with already pressed snap key when not allowed */
15457 if (player->is_snapping && !can_continue_snapping)
15460 if (player->is_snapping)
15464 player->MovDir = snap_direction;
15466 if (player->MovPos == 0)
15468 player->is_moving = FALSE;
15469 player->is_digging = FALSE;
15470 player->is_collecting = FALSE;
15473 player->is_dropping = FALSE;
15474 player->is_dropping_pressed = FALSE;
15475 player->drop_pressed_delay = 0;
15477 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15480 player->is_snapping = TRUE;
15481 player->is_active = TRUE;
15483 if (player->MovPos == 0)
15485 player->is_moving = FALSE;
15486 player->is_digging = FALSE;
15487 player->is_collecting = FALSE;
15490 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15491 TEST_DrawLevelField(player->last_jx, player->last_jy);
15493 TEST_DrawLevelField(x, y);
15498 static boolean DropElement(struct PlayerInfo *player)
15500 int old_element, new_element;
15501 int dropx = player->jx, dropy = player->jy;
15502 int drop_direction = player->MovDir;
15503 int drop_side = drop_direction;
15505 int drop_element = get_next_dropped_element(player);
15507 int drop_element = (player->inventory_size > 0 ?
15508 player->inventory_element[player->inventory_size - 1] :
15509 player->inventory_infinite_element != EL_UNDEFINED ?
15510 player->inventory_infinite_element :
15511 player->dynabombs_left > 0 ?
15512 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15516 player->is_dropping_pressed = TRUE;
15518 /* do not drop an element on top of another element; when holding drop key
15519 pressed without moving, dropped element must move away before the next
15520 element can be dropped (this is especially important if the next element
15521 is dynamite, which can be placed on background for historical reasons) */
15522 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15525 if (IS_THROWABLE(drop_element))
15527 dropx += GET_DX_FROM_DIR(drop_direction);
15528 dropy += GET_DY_FROM_DIR(drop_direction);
15530 if (!IN_LEV_FIELD(dropx, dropy))
15534 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15535 new_element = drop_element; /* default: no change when dropping */
15537 /* check if player is active, not moving and ready to drop */
15538 if (!player->active || player->MovPos || player->drop_delay > 0)
15541 /* check if player has anything that can be dropped */
15542 if (new_element == EL_UNDEFINED)
15545 /* check if drop key was pressed long enough for EM style dynamite */
15546 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15549 /* check if anything can be dropped at the current position */
15550 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15553 /* collected custom elements can only be dropped on empty fields */
15554 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15557 if (old_element != EL_EMPTY)
15558 Back[dropx][dropy] = old_element; /* store old element on this field */
15560 ResetGfxAnimation(dropx, dropy);
15561 ResetRandomAnimationValue(dropx, dropy);
15563 if (player->inventory_size > 0 ||
15564 player->inventory_infinite_element != EL_UNDEFINED)
15566 if (player->inventory_size > 0)
15568 player->inventory_size--;
15570 DrawGameDoorValues();
15572 if (new_element == EL_DYNAMITE)
15573 new_element = EL_DYNAMITE_ACTIVE;
15574 else if (new_element == EL_EM_DYNAMITE)
15575 new_element = EL_EM_DYNAMITE_ACTIVE;
15576 else if (new_element == EL_SP_DISK_RED)
15577 new_element = EL_SP_DISK_RED_ACTIVE;
15580 Feld[dropx][dropy] = new_element;
15582 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15583 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15584 el2img(Feld[dropx][dropy]), 0);
15586 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15588 /* needed if previous element just changed to "empty" in the last frame */
15589 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15591 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15592 player->index_bit, drop_side);
15593 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15595 player->index_bit, drop_side);
15597 TestIfElementTouchesCustomElement(dropx, dropy);
15599 else /* player is dropping a dyna bomb */
15601 player->dynabombs_left--;
15603 Feld[dropx][dropy] = new_element;
15605 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15606 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15607 el2img(Feld[dropx][dropy]), 0);
15609 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15612 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15613 InitField_WithBug1(dropx, dropy, FALSE);
15615 new_element = Feld[dropx][dropy]; /* element might have changed */
15617 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15618 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15620 int move_direction, nextx, nexty;
15622 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15623 MovDir[dropx][dropy] = drop_direction;
15625 move_direction = MovDir[dropx][dropy];
15626 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15627 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15629 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15631 #if USE_FIX_IMPACT_COLLISION
15632 /* do not cause impact style collision by dropping elements that can fall */
15633 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15635 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15639 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15640 player->is_dropping = TRUE;
15642 player->drop_pressed_delay = 0;
15643 player->is_dropping_pressed = FALSE;
15645 player->drop_x = dropx;
15646 player->drop_y = dropy;
15651 /* ------------------------------------------------------------------------- */
15652 /* game sound playing functions */
15653 /* ------------------------------------------------------------------------- */
15655 static int *loop_sound_frame = NULL;
15656 static int *loop_sound_volume = NULL;
15658 void InitPlayLevelSound()
15660 int num_sounds = getSoundListSize();
15662 checked_free(loop_sound_frame);
15663 checked_free(loop_sound_volume);
15665 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15666 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15669 static void PlayLevelSound(int x, int y, int nr)
15671 int sx = SCREENX(x), sy = SCREENY(y);
15672 int volume, stereo_position;
15673 int max_distance = 8;
15674 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15676 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15677 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15680 if (!IN_LEV_FIELD(x, y) ||
15681 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15682 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15685 volume = SOUND_MAX_VOLUME;
15687 if (!IN_SCR_FIELD(sx, sy))
15689 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15690 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15692 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15695 stereo_position = (SOUND_MAX_LEFT +
15696 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15697 (SCR_FIELDX + 2 * max_distance));
15699 if (IS_LOOP_SOUND(nr))
15701 /* This assures that quieter loop sounds do not overwrite louder ones,
15702 while restarting sound volume comparison with each new game frame. */
15704 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15707 loop_sound_volume[nr] = volume;
15708 loop_sound_frame[nr] = FrameCounter;
15711 PlaySoundExt(nr, volume, stereo_position, type);
15714 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15716 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15717 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15718 y < LEVELY(BY1) ? LEVELY(BY1) :
15719 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15723 static void PlayLevelSoundAction(int x, int y, int action)
15725 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15728 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15730 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15732 if (sound_effect != SND_UNDEFINED)
15733 PlayLevelSound(x, y, sound_effect);
15736 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15739 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15741 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15742 PlayLevelSound(x, y, sound_effect);
15745 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15747 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15749 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15750 PlayLevelSound(x, y, sound_effect);
15753 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15755 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15757 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15758 StopSound(sound_effect);
15761 static void PlayLevelMusic()
15763 if (levelset.music[level_nr] != MUS_UNDEFINED)
15764 PlayMusic(levelset.music[level_nr]); /* from config file */
15766 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15769 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15771 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15772 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15773 int x = xx - 1 - offset;
15774 int y = yy - 1 - offset;
15779 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15783 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15787 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15791 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15795 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15799 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15803 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15806 case SAMPLE_android_clone:
15807 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15810 case SAMPLE_android_move:
15811 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15814 case SAMPLE_spring:
15815 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15819 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15823 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15826 case SAMPLE_eater_eat:
15827 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15831 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15834 case SAMPLE_collect:
15835 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15838 case SAMPLE_diamond:
15839 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15842 case SAMPLE_squash:
15843 /* !!! CHECK THIS !!! */
15845 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15847 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15851 case SAMPLE_wonderfall:
15852 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15856 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15860 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15864 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15868 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15872 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15876 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15879 case SAMPLE_wonder:
15880 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15884 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15887 case SAMPLE_exit_open:
15888 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15891 case SAMPLE_exit_leave:
15892 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15895 case SAMPLE_dynamite:
15896 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15900 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15904 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15908 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15912 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15916 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15920 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15924 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15930 void ChangeTime(int value)
15932 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15936 /* EMC game engine uses value from time counter of RND game engine */
15937 level.native_em_level->lev->time = *time;
15939 DrawGameValue_Time(*time);
15942 void RaiseScore(int value)
15944 /* EMC game engine and RND game engine have separate score counters */
15945 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15946 &level.native_em_level->lev->score : &local_player->score);
15950 DrawGameValue_Score(*score);
15954 void RaiseScore(int value)
15956 local_player->score += value;
15959 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15961 DisplayGameControlValues();
15963 DrawGameValue_Score(local_player->score);
15967 void RaiseScoreElement(int element)
15972 case EL_BD_DIAMOND:
15973 case EL_EMERALD_YELLOW:
15974 case EL_EMERALD_RED:
15975 case EL_EMERALD_PURPLE:
15976 case EL_SP_INFOTRON:
15977 RaiseScore(level.score[SC_EMERALD]);
15980 RaiseScore(level.score[SC_DIAMOND]);
15983 RaiseScore(level.score[SC_CRYSTAL]);
15986 RaiseScore(level.score[SC_PEARL]);
15989 case EL_BD_BUTTERFLY:
15990 case EL_SP_ELECTRON:
15991 RaiseScore(level.score[SC_BUG]);
15994 case EL_BD_FIREFLY:
15995 case EL_SP_SNIKSNAK:
15996 RaiseScore(level.score[SC_SPACESHIP]);
15999 case EL_DARK_YAMYAM:
16000 RaiseScore(level.score[SC_YAMYAM]);
16003 RaiseScore(level.score[SC_ROBOT]);
16006 RaiseScore(level.score[SC_PACMAN]);
16009 RaiseScore(level.score[SC_NUT]);
16012 case EL_EM_DYNAMITE:
16013 case EL_SP_DISK_RED:
16014 case EL_DYNABOMB_INCREASE_NUMBER:
16015 case EL_DYNABOMB_INCREASE_SIZE:
16016 case EL_DYNABOMB_INCREASE_POWER:
16017 RaiseScore(level.score[SC_DYNAMITE]);
16019 case EL_SHIELD_NORMAL:
16020 case EL_SHIELD_DEADLY:
16021 RaiseScore(level.score[SC_SHIELD]);
16023 case EL_EXTRA_TIME:
16024 RaiseScore(level.extra_time_score);
16038 case EL_DC_KEY_WHITE:
16039 RaiseScore(level.score[SC_KEY]);
16042 RaiseScore(element_info[element].collect_score);
16047 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16049 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16051 #if defined(NETWORK_AVALIABLE)
16052 if (options.network)
16053 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16062 FadeSkipNextFadeIn();
16064 fading = fading_none;
16068 OpenDoor(DOOR_CLOSE_1);
16071 game_status = GAME_MODE_MAIN;
16074 DrawAndFadeInMainMenu(REDRAW_FIELD);
16082 FadeOut(REDRAW_FIELD);
16085 game_status = GAME_MODE_MAIN;
16087 DrawAndFadeInMainMenu(REDRAW_FIELD);
16091 else /* continue playing the game */
16093 if (tape.playing && tape.deactivate_display)
16094 TapeDeactivateDisplayOff(TRUE);
16096 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16098 if (tape.playing && tape.deactivate_display)
16099 TapeDeactivateDisplayOn();
16103 void RequestQuitGame(boolean ask_if_really_quit)
16105 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16106 boolean skip_request = AllPlayersGone || quick_quit;
16108 RequestQuitGameExt(skip_request, quick_quit,
16109 "Do you really want to quit the game ?");
16113 /* ------------------------------------------------------------------------- */
16114 /* random generator functions */
16115 /* ------------------------------------------------------------------------- */
16117 unsigned int InitEngineRandom_RND(long seed)
16119 game.num_random_calls = 0;
16122 unsigned int rnd_seed = InitEngineRandom(seed);
16124 printf("::: START RND: %d\n", rnd_seed);
16129 return InitEngineRandom(seed);
16135 unsigned int RND(int max)
16139 game.num_random_calls++;
16141 return GetEngineRandom(max);
16148 /* ------------------------------------------------------------------------- */
16149 /* game engine snapshot handling functions */
16150 /* ------------------------------------------------------------------------- */
16152 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16154 struct EngineSnapshotInfo
16156 /* runtime values for custom element collect score */
16157 int collect_score[NUM_CUSTOM_ELEMENTS];
16159 /* runtime values for group element choice position */
16160 int choice_pos[NUM_GROUP_ELEMENTS];
16162 /* runtime values for belt position animations */
16163 int belt_graphic[4 * NUM_BELT_PARTS];
16164 int belt_anim_mode[4 * NUM_BELT_PARTS];
16167 struct EngineSnapshotNodeInfo
16174 static struct EngineSnapshotInfo engine_snapshot_rnd;
16175 static ListNode *engine_snapshot_list = NULL;
16176 static char *snapshot_level_identifier = NULL;
16177 static int snapshot_level_nr = -1;
16179 void FreeEngineSnapshot()
16181 while (engine_snapshot_list != NULL)
16182 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16185 setString(&snapshot_level_identifier, NULL);
16186 snapshot_level_nr = -1;
16189 static void SaveEngineSnapshotValues_RND()
16191 static int belt_base_active_element[4] =
16193 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16194 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16195 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16196 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16200 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16202 int element = EL_CUSTOM_START + i;
16204 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16207 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16209 int element = EL_GROUP_START + i;
16211 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16214 for (i = 0; i < 4; i++)
16216 for (j = 0; j < NUM_BELT_PARTS; j++)
16218 int element = belt_base_active_element[i] + j;
16219 int graphic = el2img(element);
16220 int anim_mode = graphic_info[graphic].anim_mode;
16222 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16223 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16228 static void LoadEngineSnapshotValues_RND()
16230 unsigned long num_random_calls = game.num_random_calls;
16233 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16235 int element = EL_CUSTOM_START + i;
16237 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16240 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16242 int element = EL_GROUP_START + i;
16244 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16247 for (i = 0; i < 4; i++)
16249 for (j = 0; j < NUM_BELT_PARTS; j++)
16251 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16252 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16254 graphic_info[graphic].anim_mode = anim_mode;
16258 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16260 InitRND(tape.random_seed);
16261 for (i = 0; i < num_random_calls; i++)
16265 if (game.num_random_calls != num_random_calls)
16267 Error(ERR_INFO, "number of random calls out of sync");
16268 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16269 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16270 Error(ERR_EXIT, "this should not happen -- please debug");
16274 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16276 struct EngineSnapshotNodeInfo *bi =
16277 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16279 bi->buffer_orig = buffer;
16280 bi->buffer_copy = checked_malloc(size);
16283 memcpy(bi->buffer_copy, buffer, size);
16285 addNodeToList(&engine_snapshot_list, NULL, bi);
16288 void SaveEngineSnapshot()
16290 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16292 if (level_editor_test_game) /* do not save snapshots from editor */
16295 /* copy some special values to a structure better suited for the snapshot */
16297 SaveEngineSnapshotValues_RND();
16298 SaveEngineSnapshotValues_EM();
16300 /* save values stored in special snapshot structure */
16302 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16303 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16305 /* save further RND engine values */
16307 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16308 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16309 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16311 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16312 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16313 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16314 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16316 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16317 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16318 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16319 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16320 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16322 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16323 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16324 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16326 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16328 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16330 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16331 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16333 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16334 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16335 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16336 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16337 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16338 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16339 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16340 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16341 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16342 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16343 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16344 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16345 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16346 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16347 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16348 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16349 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16350 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16352 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16353 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16355 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16356 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16357 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16359 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16360 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16362 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16363 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16364 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16365 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16366 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16368 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16369 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16371 /* save level identification information */
16373 setString(&snapshot_level_identifier, leveldir_current->identifier);
16374 snapshot_level_nr = level_nr;
16377 ListNode *node = engine_snapshot_list;
16380 while (node != NULL)
16382 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16387 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16391 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16393 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16396 void LoadEngineSnapshot()
16398 ListNode *node = engine_snapshot_list;
16400 if (engine_snapshot_list == NULL)
16403 while (node != NULL)
16405 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16410 /* restore special values from snapshot structure */
16412 LoadEngineSnapshotValues_RND();
16413 LoadEngineSnapshotValues_EM();
16416 boolean CheckEngineSnapshot()
16418 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16419 snapshot_level_nr == level_nr);
16423 /* ---------- new game button stuff ---------------------------------------- */
16425 /* graphic position values for game buttons */
16426 #define GAME_BUTTON_XSIZE 30
16427 #define GAME_BUTTON_YSIZE 30
16428 #define GAME_BUTTON_XPOS 5
16429 #define GAME_BUTTON_YPOS 215
16430 #define SOUND_BUTTON_XPOS 5
16431 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16433 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16434 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16435 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16436 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16437 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16438 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16446 } gamebutton_info[NUM_GAME_BUTTONS] =
16450 &game.button.stop.x, &game.button.stop.y,
16451 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16456 &game.button.pause.x, &game.button.pause.y,
16457 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16458 GAME_CTRL_ID_PAUSE,
16462 &game.button.play.x, &game.button.play.y,
16463 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16468 &game.button.sound_music.x, &game.button.sound_music.y,
16469 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16470 SOUND_CTRL_ID_MUSIC,
16471 "background music on/off"
16474 &game.button.sound_loops.x, &game.button.sound_loops.y,
16475 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16476 SOUND_CTRL_ID_LOOPS,
16477 "sound loops on/off"
16480 &game.button.sound_simple.x,&game.button.sound_simple.y,
16481 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16482 SOUND_CTRL_ID_SIMPLE,
16483 "normal sounds on/off"
16487 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16492 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16493 GAME_CTRL_ID_PAUSE,
16497 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16502 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16503 SOUND_CTRL_ID_MUSIC,
16504 "background music on/off"
16507 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16508 SOUND_CTRL_ID_LOOPS,
16509 "sound loops on/off"
16512 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16513 SOUND_CTRL_ID_SIMPLE,
16514 "normal sounds on/off"
16519 void CreateGameButtons()
16523 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16525 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16526 struct GadgetInfo *gi;
16529 unsigned long event_mask;
16531 int gd_xoffset, gd_yoffset;
16532 int gd_x1, gd_x2, gd_y1, gd_y2;
16535 x = DX + *gamebutton_info[i].x;
16536 y = DY + *gamebutton_info[i].y;
16537 gd_xoffset = gamebutton_info[i].gd_x;
16538 gd_yoffset = gamebutton_info[i].gd_y;
16539 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16540 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16542 if (id == GAME_CTRL_ID_STOP ||
16543 id == GAME_CTRL_ID_PAUSE ||
16544 id == GAME_CTRL_ID_PLAY)
16546 button_type = GD_TYPE_NORMAL_BUTTON;
16548 event_mask = GD_EVENT_RELEASED;
16549 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16550 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16554 button_type = GD_TYPE_CHECK_BUTTON;
16556 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16557 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16558 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16559 event_mask = GD_EVENT_PRESSED;
16560 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16561 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16564 gi = CreateGadget(GDI_CUSTOM_ID, id,
16565 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16570 GDI_X, DX + gd_xoffset,
16571 GDI_Y, DY + gd_yoffset,
16573 GDI_WIDTH, GAME_BUTTON_XSIZE,
16574 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16575 GDI_TYPE, button_type,
16576 GDI_STATE, GD_BUTTON_UNPRESSED,
16577 GDI_CHECKED, checked,
16578 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16579 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16580 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16581 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16582 GDI_DIRECT_DRAW, FALSE,
16583 GDI_EVENT_MASK, event_mask,
16584 GDI_CALLBACK_ACTION, HandleGameButtons,
16588 Error(ERR_EXIT, "cannot create gadget");
16590 game_gadget[id] = gi;
16594 void FreeGameButtons()
16598 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16599 FreeGadget(game_gadget[i]);
16602 static void MapGameButtons()
16606 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16607 MapGadget(game_gadget[i]);
16610 void UnmapGameButtons()
16614 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16615 UnmapGadget(game_gadget[i]);
16618 void RedrawGameButtons()
16622 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16623 RedrawGadget(game_gadget[i]);
16626 static void HandleGameButtons(struct GadgetInfo *gi)
16628 int id = gi->custom_id;
16630 if (game_status != GAME_MODE_PLAYING)
16635 case GAME_CTRL_ID_STOP:
16639 RequestQuitGame(TRUE);
16642 case GAME_CTRL_ID_PAUSE:
16643 if (options.network)
16645 #if defined(NETWORK_AVALIABLE)
16647 SendToServer_ContinuePlaying();
16649 SendToServer_PausePlaying();
16653 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16656 case GAME_CTRL_ID_PLAY:
16659 #if defined(NETWORK_AVALIABLE)
16660 if (options.network)
16661 SendToServer_ContinuePlaying();
16665 tape.pausing = FALSE;
16666 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16671 case SOUND_CTRL_ID_MUSIC:
16672 if (setup.sound_music)
16674 setup.sound_music = FALSE;
16677 else if (audio.music_available)
16679 setup.sound = setup.sound_music = TRUE;
16681 SetAudioMode(setup.sound);
16687 case SOUND_CTRL_ID_LOOPS:
16688 if (setup.sound_loops)
16689 setup.sound_loops = FALSE;
16690 else if (audio.loops_available)
16692 setup.sound = setup.sound_loops = TRUE;
16693 SetAudioMode(setup.sound);
16697 case SOUND_CTRL_ID_SIMPLE:
16698 if (setup.sound_simple)
16699 setup.sound_simple = FALSE;
16700 else if (audio.sound_available)
16702 setup.sound = setup.sound_simple = TRUE;
16703 SetAudioMode(setup.sound);