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;
1172 static int map_player_action[MAX_PLAYERS];
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after */
1177 /* a specified time, eventually calling a function when changing */
1178 /* ------------------------------------------------------------------------- */
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1198 struct ChangingElementInfo
1203 void (*pre_change_function)(int x, int y);
1204 void (*change_function)(int x, int y);
1205 void (*post_change_function)(int x, int y);
1208 static struct ChangingElementInfo change_delay_list[] =
1243 EL_STEEL_EXIT_OPENING,
1251 EL_STEEL_EXIT_CLOSING,
1252 EL_STEEL_EXIT_CLOSED,
1279 EL_EM_STEEL_EXIT_OPENING,
1280 EL_EM_STEEL_EXIT_OPEN,
1287 EL_EM_STEEL_EXIT_CLOSING,
1291 EL_EM_STEEL_EXIT_CLOSED,
1315 EL_SWITCHGATE_OPENING,
1323 EL_SWITCHGATE_CLOSING,
1324 EL_SWITCHGATE_CLOSED,
1331 EL_TIMEGATE_OPENING,
1339 EL_TIMEGATE_CLOSING,
1348 EL_ACID_SPLASH_LEFT,
1356 EL_ACID_SPLASH_RIGHT,
1365 EL_SP_BUGGY_BASE_ACTIVATING,
1372 EL_SP_BUGGY_BASE_ACTIVATING,
1373 EL_SP_BUGGY_BASE_ACTIVE,
1380 EL_SP_BUGGY_BASE_ACTIVE,
1404 EL_ROBOT_WHEEL_ACTIVE,
1412 EL_TIMEGATE_SWITCH_ACTIVE,
1420 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421 EL_DC_TIMEGATE_SWITCH,
1428 EL_EMC_MAGIC_BALL_ACTIVE,
1429 EL_EMC_MAGIC_BALL_ACTIVE,
1436 EL_EMC_SPRING_BUMPER_ACTIVE,
1437 EL_EMC_SPRING_BUMPER,
1444 EL_DIAGONAL_SHRINKING,
1452 EL_DIAGONAL_GROWING,
1473 int push_delay_fixed, push_delay_random;
1477 { EL_SPRING, 0, 0 },
1478 { EL_BALLOON, 0, 0 },
1480 { EL_SOKOBAN_OBJECT, 2, 0 },
1481 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1482 { EL_SATELLITE, 2, 0 },
1483 { EL_SP_DISK_YELLOW, 2, 0 },
1485 { EL_UNDEFINED, 0, 0 },
1493 move_stepsize_list[] =
1495 { EL_AMOEBA_DROP, 2 },
1496 { EL_AMOEBA_DROPPING, 2 },
1497 { EL_QUICKSAND_FILLING, 1 },
1498 { EL_QUICKSAND_EMPTYING, 1 },
1499 { EL_QUICKSAND_FAST_FILLING, 2 },
1500 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501 { EL_MAGIC_WALL_FILLING, 2 },
1502 { EL_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_BD_MAGIC_WALL_FILLING, 2 },
1504 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1505 { EL_DC_MAGIC_WALL_FILLING, 2 },
1506 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1508 { EL_UNDEFINED, 0 },
1516 collect_count_list[] =
1519 { EL_BD_DIAMOND, 1 },
1520 { EL_EMERALD_YELLOW, 1 },
1521 { EL_EMERALD_RED, 1 },
1522 { EL_EMERALD_PURPLE, 1 },
1524 { EL_SP_INFOTRON, 1 },
1528 { EL_UNDEFINED, 0 },
1536 access_direction_list[] =
1538 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1540 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1541 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1542 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1543 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1544 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1545 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1546 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1547 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1548 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1550 { EL_SP_PORT_LEFT, MV_RIGHT },
1551 { EL_SP_PORT_RIGHT, MV_LEFT },
1552 { EL_SP_PORT_UP, MV_DOWN },
1553 { EL_SP_PORT_DOWN, MV_UP },
1554 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1555 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1556 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1561 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1562 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1563 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1564 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1565 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1566 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1567 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1568 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1570 { EL_UNDEFINED, MV_NONE }
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1575 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1578 IS_JUST_CHANGING(x, y))
1580 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1588 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1589 (y) >= 0 && (y) <= lev_fieldy - 1; \
1590 (y) += playfield_scan_delta_y) \
1591 for ((x) = playfield_scan_start_x; \
1592 (x) >= 0 && (x) <= lev_fieldx - 1; \
1593 (x) += playfield_scan_delta_x)
1596 void DEBUG_SetMaximumDynamite()
1600 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602 local_player->inventory_element[local_player->inventory_size++] =
1607 static void InitPlayfieldScanModeVars()
1609 if (game.use_reverse_scan_direction)
1611 playfield_scan_start_x = lev_fieldx - 1;
1612 playfield_scan_start_y = lev_fieldy - 1;
1614 playfield_scan_delta_x = -1;
1615 playfield_scan_delta_y = -1;
1619 playfield_scan_start_x = 0;
1620 playfield_scan_start_y = 0;
1622 playfield_scan_delta_x = 1;
1623 playfield_scan_delta_y = 1;
1627 static void InitPlayfieldScanMode(int mode)
1629 game.use_reverse_scan_direction =
1630 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1632 InitPlayfieldScanModeVars();
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1638 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1640 /* make sure that stepsize value is always a power of 2 */
1641 move_stepsize = (1 << log_2(move_stepsize));
1643 return TILEX / move_stepsize;
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1649 int player_nr = player->index_nr;
1650 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1653 /* do no immediately change move delay -- the player might just be moving */
1654 player->move_delay_value_next = move_delay;
1656 /* information if player can move must be set separately */
1657 player->cannot_move = cannot_move;
1661 player->move_delay = game.initial_move_delay[player_nr];
1662 player->move_delay_value = game.initial_move_delay_value[player_nr];
1664 player->move_delay_value_next = -1;
1666 player->move_delay_reset_counter = 0;
1670 void GetPlayerConfig()
1672 GameFrameDelay = setup.game_frame_delay;
1674 if (!audio.sound_available)
1675 setup.sound_simple = FALSE;
1677 if (!audio.loops_available)
1678 setup.sound_loops = FALSE;
1680 if (!audio.music_available)
1681 setup.sound_music = FALSE;
1683 if (!video.fullscreen_available)
1684 setup.fullscreen = FALSE;
1686 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1688 SetAudioMode(setup.sound);
1692 int GetElementFromGroupElement(int element)
1694 if (IS_GROUP_ELEMENT(element))
1696 struct ElementGroupInfo *group = element_info[element].group;
1697 int last_anim_random_frame = gfx.anim_random_frame;
1700 if (group->choice_mode == ANIM_RANDOM)
1701 gfx.anim_random_frame = RND(group->num_elements_resolved);
1703 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704 group->choice_mode, 0,
1707 if (group->choice_mode == ANIM_RANDOM)
1708 gfx.anim_random_frame = last_anim_random_frame;
1710 group->choice_pos++;
1712 element = group->element_resolved[element_pos];
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1720 if (element == EL_SP_MURPHY)
1724 if (stored_player[0].present)
1726 Feld[x][y] = EL_SP_MURPHY_CLONE;
1732 stored_player[0].initial_element = element;
1733 stored_player[0].use_murphy = TRUE;
1735 if (!level.use_artwork_element[0])
1736 stored_player[0].artwork_element = EL_SP_MURPHY;
1739 Feld[x][y] = EL_PLAYER_1;
1745 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746 int jx = player->jx, jy = player->jy;
1748 player->present = TRUE;
1750 player->block_last_field = (element == EL_SP_MURPHY ?
1751 level.sp_block_last_field :
1752 level.block_last_field);
1754 /* ---------- initialize player's last field block delay --------------- */
1756 /* always start with reliable default value (no adjustment needed) */
1757 player->block_delay_adjustment = 0;
1759 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760 if (player->block_last_field && element == EL_SP_MURPHY)
1761 player->block_delay_adjustment = 1;
1763 /* special case 2: in game engines before 3.1.1, blocking was different */
1764 if (game.use_block_last_field_bug)
1765 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1767 if (!options.network || player->connected)
1769 player->active = TRUE;
1771 /* remove potentially duplicate players */
1772 if (StorePlayer[jx][jy] == Feld[x][y])
1773 StorePlayer[jx][jy] = 0;
1775 StorePlayer[x][y] = Feld[x][y];
1779 printf("Player %d activated.\n", player->element_nr);
1780 printf("[Local player is %d and currently %s.]\n",
1781 local_player->element_nr,
1782 local_player->active ? "active" : "not active");
1786 Feld[x][y] = EL_EMPTY;
1788 player->jx = player->last_jx = x;
1789 player->jy = player->last_jy = y;
1792 #if USE_PLAYER_REANIMATION
1795 int player_nr = GET_PLAYER_NR(element);
1796 struct PlayerInfo *player = &stored_player[player_nr];
1798 if (player->active && player->killed)
1799 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1804 static void InitField(int x, int y, boolean init_game)
1806 int element = Feld[x][y];
1815 InitPlayerField(x, y, element, init_game);
1818 case EL_SOKOBAN_FIELD_PLAYER:
1819 element = Feld[x][y] = EL_PLAYER_1;
1820 InitField(x, y, init_game);
1822 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823 InitField(x, y, init_game);
1826 case EL_SOKOBAN_FIELD_EMPTY:
1827 local_player->sokobanfields_still_needed++;
1831 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1849 case EL_SPACESHIP_RIGHT:
1850 case EL_SPACESHIP_UP:
1851 case EL_SPACESHIP_LEFT:
1852 case EL_SPACESHIP_DOWN:
1853 case EL_BD_BUTTERFLY:
1854 case EL_BD_BUTTERFLY_RIGHT:
1855 case EL_BD_BUTTERFLY_UP:
1856 case EL_BD_BUTTERFLY_LEFT:
1857 case EL_BD_BUTTERFLY_DOWN:
1859 case EL_BD_FIREFLY_RIGHT:
1860 case EL_BD_FIREFLY_UP:
1861 case EL_BD_FIREFLY_LEFT:
1862 case EL_BD_FIREFLY_DOWN:
1863 case EL_PACMAN_RIGHT:
1865 case EL_PACMAN_LEFT:
1866 case EL_PACMAN_DOWN:
1868 case EL_YAMYAM_LEFT:
1869 case EL_YAMYAM_RIGHT:
1871 case EL_YAMYAM_DOWN:
1872 case EL_DARK_YAMYAM:
1875 case EL_SP_SNIKSNAK:
1876 case EL_SP_ELECTRON:
1885 case EL_AMOEBA_FULL:
1890 case EL_AMOEBA_DROP:
1891 if (y == lev_fieldy - 1)
1893 Feld[x][y] = EL_AMOEBA_GROWING;
1894 Store[x][y] = EL_AMOEBA_WET;
1898 case EL_DYNAMITE_ACTIVE:
1899 case EL_SP_DISK_RED_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904 MovDelay[x][y] = 96;
1907 case EL_EM_DYNAMITE_ACTIVE:
1908 MovDelay[x][y] = 32;
1912 local_player->lights_still_needed++;
1916 local_player->friends_still_needed++;
1921 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1924 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1938 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1942 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1944 game.belt_dir[belt_nr] = belt_dir;
1945 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1947 else /* more than one switch -- set it like the first switch */
1949 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1957 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1960 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1962 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1966 case EL_LIGHT_SWITCH_ACTIVE:
1968 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1971 case EL_INVISIBLE_STEELWALL:
1972 case EL_INVISIBLE_WALL:
1973 case EL_INVISIBLE_SAND:
1974 if (game.light_time_left > 0 ||
1975 game.lenses_time_left > 0)
1976 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1979 case EL_EMC_MAGIC_BALL:
1980 if (game.ball_state)
1981 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1984 case EL_EMC_MAGIC_BALL_SWITCH:
1985 if (game.ball_state)
1986 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1989 case EL_TRIGGER_PLAYER:
1990 case EL_TRIGGER_ELEMENT:
1991 case EL_TRIGGER_CE_VALUE:
1992 case EL_TRIGGER_CE_SCORE:
1994 case EL_ANY_ELEMENT:
1995 case EL_CURRENT_CE_VALUE:
1996 case EL_CURRENT_CE_SCORE:
2013 /* reference elements should not be used on the playfield */
2014 Feld[x][y] = EL_EMPTY;
2018 if (IS_CUSTOM_ELEMENT(element))
2020 if (CAN_MOVE(element))
2023 #if USE_NEW_CUSTOM_VALUE
2024 if (!element_info[element].use_last_ce_value || init_game)
2025 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2028 else if (IS_GROUP_ELEMENT(element))
2030 Feld[x][y] = GetElementFromGroupElement(element);
2032 InitField(x, y, init_game);
2039 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2044 InitField(x, y, init_game);
2046 /* not needed to call InitMovDir() -- already done by InitField()! */
2047 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048 CAN_MOVE(Feld[x][y]))
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2054 int old_element = Feld[x][y];
2056 InitField(x, y, init_game);
2058 /* not needed to call InitMovDir() -- already done by InitField()! */
2059 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060 CAN_MOVE(old_element) &&
2061 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2064 /* this case is in fact a combination of not less than three bugs:
2065 first, it calls InitMovDir() for elements that can move, although this is
2066 already done by InitField(); then, it checks the element that was at this
2067 field _before_ the call to InitField() (which can change it); lastly, it
2068 was not called for "mole with direction" elements, which were treated as
2069 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075 static int get_key_element_from_nr(int key_nr)
2077 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079 EL_EM_KEY_1 : EL_KEY_1);
2081 return key_base_element + key_nr;
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2086 return (player->inventory_size > 0 ?
2087 player->inventory_element[player->inventory_size - 1] :
2088 player->inventory_infinite_element != EL_UNDEFINED ?
2089 player->inventory_infinite_element :
2090 player->dynabombs_left > 0 ?
2091 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2097 /* pos >= 0: get element from bottom of the stack;
2098 pos < 0: get element from top of the stack */
2102 int min_inventory_size = -pos;
2103 int inventory_pos = player->inventory_size - min_inventory_size;
2104 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2106 return (player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2108 player->inventory_infinite_element != EL_UNDEFINED ?
2109 player->inventory_infinite_element :
2110 player->dynabombs_left >= min_dynabombs_left ?
2111 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116 int min_dynabombs_left = pos + 1;
2117 int min_inventory_size = pos + 1 - player->dynabombs_left;
2118 int inventory_pos = pos - player->dynabombs_left;
2120 return (player->inventory_infinite_element != EL_UNDEFINED ?
2121 player->inventory_infinite_element :
2122 player->dynabombs_left >= min_dynabombs_left ?
2123 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124 player->inventory_size >= min_inventory_size ?
2125 player->inventory_element[inventory_pos] :
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2132 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2136 if (gpo1->sort_priority != gpo2->sort_priority)
2137 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2139 compare_result = gpo1->nr - gpo2->nr;
2141 return compare_result;
2144 void InitGameControlValues()
2148 for (i = 0; game_panel_controls[i].nr != -1; i++)
2150 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152 struct TextPosInfo *pos = gpc->pos;
2154 int type = gpc->type;
2158 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159 Error(ERR_EXIT, "this should not happen -- please debug");
2162 /* force update of game controls after initialization */
2163 gpc->value = gpc->last_value = -1;
2164 gpc->frame = gpc->last_frame = -1;
2165 gpc->gfx_frame = -1;
2167 /* determine panel value width for later calculation of alignment */
2168 if (type == TYPE_INTEGER || type == TYPE_STRING)
2170 pos->width = pos->size * getFontWidth(pos->font);
2171 pos->height = getFontHeight(pos->font);
2173 else if (type == TYPE_ELEMENT)
2175 pos->width = pos->size;
2176 pos->height = pos->size;
2179 /* fill structure for game panel draw order */
2181 gpo->sort_priority = pos->sort_priority;
2184 /* sort game panel controls according to sort_priority and control number */
2185 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2189 void UpdatePlayfieldElementCount()
2191 boolean use_element_count = FALSE;
2194 /* first check if it is needed at all to calculate playfield element count */
2195 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197 use_element_count = TRUE;
2199 if (!use_element_count)
2202 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 element_info[i].element_count = 0;
2205 SCAN_PLAYFIELD(x, y)
2207 element_info[Feld[x][y]].element_count++;
2210 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212 if (IS_IN_GROUP(j, i))
2213 element_info[EL_GROUP_START + i].element_count +=
2214 element_info[j].element_count;
2217 void UpdateGameControlValues()
2220 int time = (local_player->LevelSolved ?
2221 local_player->LevelSolved_CountingTime :
2222 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223 level.native_em_level->lev->time :
2224 level.time == 0 ? TimePlayed : TimeLeft);
2225 int score = (local_player->LevelSolved ?
2226 local_player->LevelSolved_CountingScore :
2227 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228 level.native_em_level->lev->score :
2229 local_player->score);
2230 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231 level.native_em_level->lev->required :
2232 local_player->gems_still_needed);
2233 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234 level.native_em_level->lev->required > 0 :
2235 local_player->gems_still_needed > 0 ||
2236 local_player->sokobanfields_still_needed > 0 ||
2237 local_player->lights_still_needed > 0);
2239 UpdatePlayfieldElementCount();
2241 /* update game panel control values */
2243 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247 for (i = 0; i < MAX_NUM_KEYS; i++)
2248 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2252 if (game.centered_player_nr == -1)
2254 for (i = 0; i < MAX_PLAYERS; i++)
2256 for (k = 0; k < MAX_NUM_KEYS; k++)
2258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2260 if (level.native_em_level->ply[i]->keys & (1 << k))
2261 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262 get_key_element_from_nr(k);
2264 else if (stored_player[i].key[k])
2265 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266 get_key_element_from_nr(k);
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271 level.native_em_level->ply[i]->dynamite;
2273 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274 stored_player[i].inventory_size;
2276 if (stored_player[i].num_white_keys > 0)
2277 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2280 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281 stored_player[i].num_white_keys;
2286 int player_nr = game.centered_player_nr;
2288 for (k = 0; k < MAX_NUM_KEYS; k++)
2290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2292 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2296 else if (stored_player[player_nr].key[k])
2297 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298 get_key_element_from_nr(k);
2301 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303 level.native_em_level->ply[player_nr]->dynamite;
2305 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306 stored_player[player_nr].inventory_size;
2308 if (stored_player[player_nr].num_white_keys > 0)
2309 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2311 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312 stored_player[player_nr].num_white_keys;
2315 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2317 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318 get_inventory_element_from_pos(local_player, i);
2319 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320 get_inventory_element_from_pos(local_player, -i - 1);
2323 game_panel_controls[GAME_PANEL_SCORE].value = score;
2324 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2326 game_panel_controls[GAME_PANEL_TIME].value = time;
2328 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2332 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2335 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336 local_player->shield_normal_time_left;
2337 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2340 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341 local_player->shield_deadly_time_left;
2343 game_panel_controls[GAME_PANEL_EXIT].value =
2344 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2346 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350 EL_EMC_MAGIC_BALL_SWITCH);
2352 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355 game.light_time_left;
2357 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360 game.timegate_time_left;
2362 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2365 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368 game.lenses_time_left;
2370 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373 game.magnify_time_left;
2375 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2377 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2379 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2380 EL_BALLOON_SWITCH_NONE);
2382 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383 local_player->dynabomb_count;
2384 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385 local_player->dynabomb_size;
2386 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2389 game_panel_controls[GAME_PANEL_PENGUINS].value =
2390 local_player->friends_still_needed;
2392 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393 local_player->sokobanfields_still_needed;
2394 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395 local_player->sokobanfields_still_needed;
2397 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2400 for (i = 0; i < NUM_BELTS; i++)
2402 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2409 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412 game.magic_wall_time_left;
2414 #if USE_PLAYER_GRAVITY
2415 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416 local_player->gravity;
2418 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2421 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2424 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427 game.panel.element[i].id : EL_UNDEFINED);
2429 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432 element_info[game.panel.element_count[i].id].element_count : 0);
2434 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437 element_info[game.panel.ce_score[i].id].collect_score : 0);
2439 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442 element_info[game.panel.ce_score_element[i].id].collect_score :
2445 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2449 /* update game panel control frames */
2451 for (i = 0; game_panel_controls[i].nr != -1; i++)
2453 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2455 if (gpc->type == TYPE_ELEMENT)
2457 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2459 int last_anim_random_frame = gfx.anim_random_frame;
2460 int element = gpc->value;
2461 int graphic = el2panelimg(element);
2463 if (gpc->value != gpc->last_value)
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474 gpc->gfx_random = INIT_GFX_RANDOM();
2477 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478 gfx.anim_random_frame = gpc->gfx_random;
2480 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481 gpc->gfx_frame = element_info[element].collect_score;
2483 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2486 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487 gfx.anim_random_frame = last_anim_random_frame;
2493 void DisplayGameControlValues()
2495 boolean redraw_panel = FALSE;
2498 for (i = 0; game_panel_controls[i].nr != -1; i++)
2500 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2502 if (PANEL_DEACTIVATED(gpc->pos))
2505 if (gpc->value == gpc->last_value &&
2506 gpc->frame == gpc->last_frame)
2509 redraw_panel = TRUE;
2515 /* copy default game door content to main double buffer */
2516 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2519 /* redraw game control buttons */
2521 RedrawGameButtons();
2527 game_status = GAME_MODE_PSEUDO_PANEL;
2530 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 int nr = game_panel_order[i].nr;
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2542 struct TextPosInfo *pos = gpc->pos;
2543 int type = gpc->type;
2544 int value = gpc->value;
2545 int frame = gpc->frame;
2547 int last_value = gpc->last_value;
2548 int last_frame = gpc->last_frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2559 if (value == last_value && frame == last_frame)
2563 gpc->last_value = value;
2564 gpc->last_frame = frame;
2567 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2570 if (type == TYPE_INTEGER)
2572 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573 nr == GAME_PANEL_TIME)
2575 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2577 if (use_dynamic_size) /* use dynamic number of digits */
2579 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581 int size2 = size1 + 1;
2582 int font1 = pos->font;
2583 int font2 = pos->font_alt;
2585 size = (value < value_change ? size1 : size2);
2586 font = (value < value_change ? font1 : font2);
2589 /* clear background if value just changed its size (dynamic digits) */
2590 if ((last_value < value_change) != (value < value_change))
2592 int width1 = size1 * getFontWidth(font1);
2593 int width2 = size2 * getFontWidth(font2);
2594 int max_width = MAX(width1, width2);
2595 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2597 pos->width = max_width;
2599 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600 max_width, max_height);
2607 /* correct text size if "digits" is zero or less */
2609 size = strlen(int2str(value, size));
2611 /* dynamically correct text alignment */
2612 pos->width = size * getFontWidth(font);
2615 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616 int2str(value, size), font, mask_mode);
2618 else if (type == TYPE_ELEMENT)
2620 int element, graphic;
2624 int dst_x = PANEL_XPOS(pos);
2625 int dst_y = PANEL_YPOS(pos);
2628 if (value != EL_UNDEFINED && value != EL_EMPTY)
2631 graphic = el2panelimg(value);
2633 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2636 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2640 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2643 width = graphic_info[graphic].width * size / TILESIZE;
2644 height = graphic_info[graphic].height * size / TILESIZE;
2648 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649 dst_x - src_x, dst_y - src_y);
2650 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660 if (value == EL_UNDEFINED || value == EL_EMPTY)
2662 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663 graphic = el2panelimg(element);
2665 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2672 graphic = el2panelimg(value);
2674 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2677 width = graphic_info[graphic].width * size / TILESIZE;
2678 height = graphic_info[graphic].height * size / TILESIZE;
2680 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2683 else if (type == TYPE_STRING)
2685 boolean active = (value != 0);
2686 char *state_normal = "off";
2687 char *state_active = "on";
2688 char *state = (active ? state_active : state_normal);
2689 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2691 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2692 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2694 if (nr == GAME_PANEL_GRAVITY_STATE)
2696 int font1 = pos->font; /* (used for normal state) */
2697 int font2 = pos->font_alt; /* (used for active state) */
2699 int size1 = strlen(state_normal);
2700 int size2 = strlen(state_active);
2701 int width1 = size1 * getFontWidth(font1);
2702 int width2 = size2 * getFontWidth(font2);
2703 int max_width = MAX(width1, width2);
2704 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2706 pos->width = max_width;
2708 /* clear background for values that may have changed its size */
2709 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710 max_width, max_height);
2713 font = (active ? font2 : font1);
2723 /* don't truncate output if "chars" is zero or less */
2726 /* dynamically correct text alignment */
2727 pos->width = size * getFontWidth(font);
2731 s_cut = getStringCopyN(s, size);
2733 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734 s_cut, font, mask_mode);
2740 redraw_mask |= REDRAW_DOOR_1;
2743 game_status = GAME_MODE_PLAYING;
2746 void UpdateAndDisplayGameControlValues()
2748 if (tape.warp_forward)
2751 UpdateGameControlValues();
2752 DisplayGameControlValues();
2755 void DrawGameValue_Emeralds(int value)
2757 struct TextPosInfo *pos = &game.panel.gems;
2759 int font_nr = pos->font;
2761 int font_nr = FONT_TEXT_2;
2763 int font_width = getFontWidth(font_nr);
2764 int chars = pos->size;
2767 return; /* !!! USE NEW STUFF !!! */
2770 if (PANEL_DEACTIVATED(pos))
2773 pos->width = chars * font_width;
2775 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2778 void DrawGameValue_Dynamite(int value)
2780 struct TextPosInfo *pos = &game.panel.inventory_count;
2782 int font_nr = pos->font;
2784 int font_nr = FONT_TEXT_2;
2786 int font_width = getFontWidth(font_nr);
2787 int chars = pos->size;
2790 return; /* !!! USE NEW STUFF !!! */
2793 if (PANEL_DEACTIVATED(pos))
2796 pos->width = chars * font_width;
2798 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 void DrawGameValue_Score(int value)
2803 struct TextPosInfo *pos = &game.panel.score;
2805 int font_nr = pos->font;
2807 int font_nr = FONT_TEXT_2;
2809 int font_width = getFontWidth(font_nr);
2810 int chars = pos->size;
2813 return; /* !!! USE NEW STUFF !!! */
2816 if (PANEL_DEACTIVATED(pos))
2819 pos->width = chars * font_width;
2821 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2824 void DrawGameValue_Time(int value)
2826 struct TextPosInfo *pos = &game.panel.time;
2827 static int last_value = -1;
2830 int chars = pos->size;
2832 int font1_nr = pos->font;
2833 int font2_nr = pos->font_alt;
2835 int font1_nr = FONT_TEXT_2;
2836 int font2_nr = FONT_TEXT_1;
2838 int font_nr = font1_nr;
2839 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2842 return; /* !!! USE NEW STUFF !!! */
2845 if (PANEL_DEACTIVATED(pos))
2848 if (use_dynamic_chars) /* use dynamic number of chars */
2850 chars = (value < 1000 ? chars1 : chars2);
2851 font_nr = (value < 1000 ? font1_nr : font2_nr);
2854 /* clear background if value just changed its size (dynamic chars only) */
2855 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2857 int width1 = chars1 * getFontWidth(font1_nr);
2858 int width2 = chars2 * getFontWidth(font2_nr);
2859 int max_width = MAX(width1, width2);
2860 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2862 pos->width = max_width;
2864 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865 max_width, max_height);
2868 pos->width = chars * getFontWidth(font_nr);
2870 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2875 void DrawGameValue_Level(int value)
2877 struct TextPosInfo *pos = &game.panel.level_number;
2880 int chars = pos->size;
2882 int font1_nr = pos->font;
2883 int font2_nr = pos->font_alt;
2885 int font1_nr = FONT_TEXT_2;
2886 int font2_nr = FONT_TEXT_1;
2888 int font_nr = font1_nr;
2889 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2892 return; /* !!! USE NEW STUFF !!! */
2895 if (PANEL_DEACTIVATED(pos))
2898 if (use_dynamic_chars) /* use dynamic number of chars */
2900 chars = (level_nr < 100 ? chars1 : chars2);
2901 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2904 pos->width = chars * getFontWidth(font_nr);
2906 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2912 struct TextPosInfo *pos = &game.panel.keys;
2915 int base_key_graphic = EL_KEY_1;
2920 return; /* !!! USE NEW STUFF !!! */
2924 if (PANEL_DEACTIVATED(pos))
2929 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930 base_key_graphic = EL_EM_KEY_1;
2934 pos->width = 4 * MINI_TILEX;
2938 for (i = 0; i < MAX_NUM_KEYS; i++)
2940 /* currently only 4 of 8 possible keys are displayed */
2941 for (i = 0; i < STD_NUM_KEYS; i++)
2945 struct TextPosInfo *pos = &game.panel.key[i];
2947 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948 int src_y = DOOR_GFX_PAGEY1 + 123;
2950 int dst_x = PANEL_XPOS(pos);
2951 int dst_y = PANEL_YPOS(pos);
2953 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954 int dst_y = PANEL_YPOS(pos);
2958 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2961 int graphic = el2edimg(element);
2965 if (PANEL_DEACTIVATED(pos))
2970 /* masked blit with tiles from half-size scaled bitmap does not work yet
2971 (no mask bitmap created for these sizes after loading and scaling) --
2972 solution: load without creating mask, scale, then create final mask */
2974 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2980 int graphic = el2edimg(base_key_graphic + i);
2985 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2987 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988 dst_x - src_x, dst_y - src_y);
2989 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2995 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2997 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3001 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3003 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3012 void DrawGameValue_Emeralds(int value)
3014 int font_nr = FONT_TEXT_2;
3015 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3017 if (PANEL_DEACTIVATED(game.panel.gems))
3020 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3023 void DrawGameValue_Dynamite(int value)
3025 int font_nr = FONT_TEXT_2;
3026 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3028 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3031 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3034 void DrawGameValue_Score(int value)
3036 int font_nr = FONT_TEXT_2;
3037 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3039 if (PANEL_DEACTIVATED(game.panel.score))
3042 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3045 void DrawGameValue_Time(int value)
3047 int font1_nr = FONT_TEXT_2;
3049 int font2_nr = FONT_TEXT_1;
3051 int font2_nr = FONT_LEVEL_NUMBER;
3053 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3056 if (PANEL_DEACTIVATED(game.panel.time))
3059 /* clear background if value just changed its size */
3060 if (value == 999 || value == 1000)
3061 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3064 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3066 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3069 void DrawGameValue_Level(int value)
3071 int font1_nr = FONT_TEXT_2;
3073 int font2_nr = FONT_TEXT_1;
3075 int font2_nr = FONT_LEVEL_NUMBER;
3078 if (PANEL_DEACTIVATED(game.panel.level))
3082 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3084 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3089 int base_key_graphic = EL_KEY_1;
3092 if (PANEL_DEACTIVATED(game.panel.keys))
3095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096 base_key_graphic = EL_EM_KEY_1;
3098 /* currently only 4 of 8 possible keys are displayed */
3099 for (i = 0; i < STD_NUM_KEYS; i++)
3101 int x = XX_KEYS + i * MINI_TILEX;
3105 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3107 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3117 int key[MAX_NUM_KEYS];
3120 /* prevent EM engine from updating time/score values parallel to GameWon() */
3121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122 local_player->LevelSolved)
3125 for (i = 0; i < MAX_NUM_KEYS; i++)
3126 key[i] = key_bits & (1 << i);
3128 DrawGameValue_Level(level_nr);
3130 DrawGameValue_Emeralds(emeralds);
3131 DrawGameValue_Dynamite(dynamite);
3132 DrawGameValue_Score(score);
3133 DrawGameValue_Time(time);
3135 DrawGameValue_Keys(key);
3138 void UpdateGameDoorValues()
3140 UpdateGameControlValues();
3143 void DrawGameDoorValues()
3145 DisplayGameControlValues();
3148 void DrawGameDoorValues_OLD()
3150 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151 int dynamite_value = 0;
3152 int score_value = (local_player->LevelSolved ? local_player->score_final :
3153 local_player->score);
3154 int gems_value = local_player->gems_still_needed;
3158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3160 DrawGameDoorValues_EM();
3165 if (game.centered_player_nr == -1)
3167 for (i = 0; i < MAX_PLAYERS; i++)
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 if (stored_player[i].key[j])
3171 key_bits |= (1 << j);
3173 dynamite_value += stored_player[i].inventory_size;
3178 int player_nr = game.centered_player_nr;
3180 for (i = 0; i < MAX_NUM_KEYS; i++)
3181 if (stored_player[player_nr].key[i])
3182 key_bits |= (1 << i);
3184 dynamite_value = stored_player[player_nr].inventory_size;
3187 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3193 =============================================================================
3195 -----------------------------------------------------------------------------
3196 initialize game engine due to level / tape version number
3197 =============================================================================
3200 static void InitGameEngine()
3202 int i, j, k, l, x, y;
3204 /* set game engine from tape file when re-playing, else from level file */
3205 game.engine_version = (tape.playing ? tape.engine_version :
3206 level.game_version);
3208 /* ---------------------------------------------------------------------- */
3209 /* set flags for bugs and changes according to active game engine version */
3210 /* ---------------------------------------------------------------------- */
3213 Summary of bugfix/change:
3214 Fixed handling for custom elements that change when pushed by the player.
3216 Fixed/changed in version:
3220 Before 3.1.0, custom elements that "change when pushing" changed directly
3221 after the player started pushing them (until then handled in "DigField()").
3222 Since 3.1.0, these custom elements are not changed until the "pushing"
3223 move of the element is finished (now handled in "ContinueMoving()").
3225 Affected levels/tapes:
3226 The first condition is generally needed for all levels/tapes before version
3227 3.1.0, which might use the old behaviour before it was changed; known tapes
3228 that are affected are some tapes from the level set "Walpurgis Gardens" by
3230 The second condition is an exception from the above case and is needed for
3231 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232 above (including some development versions of 3.1.0), but before it was
3233 known that this change would break tapes like the above and was fixed in
3234 3.1.1, so that the changed behaviour was active although the engine version
3235 while recording maybe was before 3.1.0. There is at least one tape that is
3236 affected by this exception, which is the tape for the one-level set "Bug
3237 Machine" by Juergen Bonhagen.
3240 game.use_change_when_pushing_bug =
3241 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3243 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244 tape.game_version < VERSION_IDENT(3,1,1,0)));
3247 Summary of bugfix/change:
3248 Fixed handling for blocking the field the player leaves when moving.
3250 Fixed/changed in version:
3254 Before 3.1.1, when "block last field when moving" was enabled, the field
3255 the player is leaving when moving was blocked for the time of the move,
3256 and was directly unblocked afterwards. This resulted in the last field
3257 being blocked for exactly one less than the number of frames of one player
3258 move. Additionally, even when blocking was disabled, the last field was
3259 blocked for exactly one frame.
3260 Since 3.1.1, due to changes in player movement handling, the last field
3261 is not blocked at all when blocking is disabled. When blocking is enabled,
3262 the last field is blocked for exactly the number of frames of one player
3263 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264 last field is blocked for exactly one more than the number of frames of
3267 Affected levels/tapes:
3268 (!!! yet to be determined -- probably many !!!)
3271 game.use_block_last_field_bug =
3272 (game.engine_version < VERSION_IDENT(3,1,1,0));
3275 Summary of bugfix/change:
3276 Changed behaviour of CE changes with multiple changes per single frame.
3278 Fixed/changed in version:
3282 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283 This resulted in race conditions where CEs seem to behave strange in some
3284 situations (where triggered CE changes were just skipped because there was
3285 already a CE change on that tile in the playfield in that engine frame).
3286 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287 (The number of changes per frame must be limited in any case, because else
3288 it is easily possible to define CE changes that would result in an infinite
3289 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290 should be set large enough so that it would only be reached in cases where
3291 the corresponding CE change conditions run into a loop. Therefore, it seems
3292 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293 maximal number of change pages for custom elements.)
3295 Affected levels/tapes:
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300 game.max_num_changes_per_frame = 1;
3302 game.max_num_changes_per_frame =
3303 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3306 /* ---------------------------------------------------------------------- */
3308 /* default scan direction: scan playfield from top/left to bottom/right */
3309 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3311 /* dynamically adjust element properties according to game engine version */
3312 InitElementPropertiesEngine(game.engine_version);
3315 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316 printf(" tape version == %06d [%s] [file: %06d]\n",
3317 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3319 printf(" => game.engine_version == %06d\n", game.engine_version);
3322 /* ---------- initialize player's initial move delay --------------------- */
3324 /* dynamically adjust player properties according to level information */
3325 for (i = 0; i < MAX_PLAYERS; i++)
3326 game.initial_move_delay_value[i] =
3327 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3329 /* dynamically adjust player properties according to game engine version */
3330 for (i = 0; i < MAX_PLAYERS; i++)
3331 game.initial_move_delay[i] =
3332 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333 game.initial_move_delay_value[i] : 0);
3335 /* ---------- initialize player's initial push delay --------------------- */
3337 /* dynamically adjust player properties according to game engine version */
3338 game.initial_push_delay_value =
3339 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3341 /* ---------- initialize changing elements ------------------------------- */
3343 /* initialize changing elements information */
3344 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346 struct ElementInfo *ei = &element_info[i];
3348 /* this pointer might have been changed in the level editor */
3349 ei->change = &ei->change_page[0];
3351 if (!IS_CUSTOM_ELEMENT(i))
3353 ei->change->target_element = EL_EMPTY_SPACE;
3354 ei->change->delay_fixed = 0;
3355 ei->change->delay_random = 0;
3356 ei->change->delay_frames = 1;
3359 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3361 ei->has_change_event[j] = FALSE;
3363 ei->event_page_nr[j] = 0;
3364 ei->event_page[j] = &ei->change_page[0];
3368 /* add changing elements from pre-defined list */
3369 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3371 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372 struct ElementInfo *ei = &element_info[ch_delay->element];
3374 ei->change->target_element = ch_delay->target_element;
3375 ei->change->delay_fixed = ch_delay->change_delay;
3377 ei->change->pre_change_function = ch_delay->pre_change_function;
3378 ei->change->change_function = ch_delay->change_function;
3379 ei->change->post_change_function = ch_delay->post_change_function;
3381 ei->change->can_change = TRUE;
3382 ei->change->can_change_or_has_action = TRUE;
3384 ei->has_change_event[CE_DELAY] = TRUE;
3386 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3390 /* ---------- initialize internal run-time variables --------------------- */
3392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3394 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3396 for (j = 0; j < ei->num_change_pages; j++)
3398 ei->change_page[j].can_change_or_has_action =
3399 (ei->change_page[j].can_change |
3400 ei->change_page[j].has_action);
3404 /* add change events from custom element configuration */
3405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3407 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3409 for (j = 0; j < ei->num_change_pages; j++)
3411 if (!ei->change_page[j].can_change_or_has_action)
3414 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3416 /* only add event page for the first page found with this event */
3417 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3419 ei->has_change_event[k] = TRUE;
3421 ei->event_page_nr[k] = j;
3422 ei->event_page[k] = &ei->change_page[j];
3429 /* ---------- initialize reference elements in change conditions --------- */
3431 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3433 int element = EL_CUSTOM_START + i;
3434 struct ElementInfo *ei = &element_info[element];
3436 for (j = 0; j < ei->num_change_pages; j++)
3438 int trigger_element = ei->change_page[j].initial_trigger_element;
3440 if (trigger_element >= EL_PREV_CE_8 &&
3441 trigger_element <= EL_NEXT_CE_8)
3442 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3444 ei->change_page[j].trigger_element = trigger_element;
3449 /* ---------- initialize run-time trigger player and element ------------- */
3451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3453 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3455 for (j = 0; j < ei->num_change_pages; j++)
3457 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461 ei->change_page[j].actual_trigger_ce_value = 0;
3462 ei->change_page[j].actual_trigger_ce_score = 0;
3466 /* ---------- initialize trigger events ---------------------------------- */
3468 /* initialize trigger events information */
3469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471 trigger_events[i][j] = FALSE;
3473 /* add trigger events from element change event properties */
3474 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3476 struct ElementInfo *ei = &element_info[i];
3478 for (j = 0; j < ei->num_change_pages; j++)
3480 if (!ei->change_page[j].can_change_or_has_action)
3483 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3485 int trigger_element = ei->change_page[j].trigger_element;
3487 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3489 if (ei->change_page[j].has_event[k])
3491 if (IS_GROUP_ELEMENT(trigger_element))
3493 struct ElementGroupInfo *group =
3494 element_info[trigger_element].group;
3496 for (l = 0; l < group->num_elements_resolved; l++)
3497 trigger_events[group->element_resolved[l]][k] = TRUE;
3499 else if (trigger_element == EL_ANY_ELEMENT)
3500 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501 trigger_events[l][k] = TRUE;
3503 trigger_events[trigger_element][k] = TRUE;
3510 /* ---------- initialize push delay -------------------------------------- */
3512 /* initialize push delay values to default */
3513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3515 if (!IS_CUSTOM_ELEMENT(i))
3517 /* set default push delay values (corrected since version 3.0.7-1) */
3518 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3520 element_info[i].push_delay_fixed = 2;
3521 element_info[i].push_delay_random = 8;
3525 element_info[i].push_delay_fixed = 8;
3526 element_info[i].push_delay_random = 8;
3531 /* set push delay value for certain elements from pre-defined list */
3532 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3534 int e = push_delay_list[i].element;
3536 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3537 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3540 /* set push delay value for Supaplex elements for newer engine versions */
3541 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3543 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3545 if (IS_SP_ELEMENT(i))
3547 /* set SP push delay to just enough to push under a falling zonk */
3548 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3550 element_info[i].push_delay_fixed = delay;
3551 element_info[i].push_delay_random = 0;
3556 /* ---------- initialize move stepsize ----------------------------------- */
3558 /* initialize move stepsize values to default */
3559 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560 if (!IS_CUSTOM_ELEMENT(i))
3561 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3563 /* set move stepsize value for certain elements from pre-defined list */
3564 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3566 int e = move_stepsize_list[i].element;
3568 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3571 /* ---------- initialize collect score ----------------------------------- */
3573 /* initialize collect score values for custom elements from initial value */
3574 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575 if (IS_CUSTOM_ELEMENT(i))
3576 element_info[i].collect_score = element_info[i].collect_score_initial;
3578 /* ---------- initialize collect count ----------------------------------- */
3580 /* initialize collect count values for non-custom elements */
3581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582 if (!IS_CUSTOM_ELEMENT(i))
3583 element_info[i].collect_count_initial = 0;
3585 /* add collect count values for all elements from pre-defined list */
3586 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587 element_info[collect_count_list[i].element].collect_count_initial =
3588 collect_count_list[i].count;
3590 /* ---------- initialize access direction -------------------------------- */
3592 /* initialize access direction values to default (access from every side) */
3593 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594 if (!IS_CUSTOM_ELEMENT(i))
3595 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3597 /* set access direction value for certain elements from pre-defined list */
3598 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599 element_info[access_direction_list[i].element].access_direction =
3600 access_direction_list[i].direction;
3602 /* ---------- initialize explosion content ------------------------------- */
3603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3605 if (IS_CUSTOM_ELEMENT(i))
3608 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3610 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3612 element_info[i].content.e[x][y] =
3613 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615 i == EL_PLAYER_3 ? EL_EMERALD :
3616 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617 i == EL_MOLE ? EL_EMERALD_RED :
3618 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623 i == EL_WALL_EMERALD ? EL_EMERALD :
3624 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629 i == EL_WALL_PEARL ? EL_PEARL :
3630 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3635 /* ---------- initialize recursion detection ------------------------------ */
3636 recursion_loop_depth = 0;
3637 recursion_loop_detected = FALSE;
3638 recursion_loop_element = EL_UNDEFINED;
3640 /* ---------- initialize graphics engine ---------------------------------- */
3641 game.scroll_delay_value =
3642 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643 setup.scroll_delay ? setup.scroll_delay_value : 0);
3644 game.scroll_delay_value =
3645 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3648 int get_num_special_action(int element, int action_first, int action_last)
3650 int num_special_action = 0;
3653 for (i = action_first; i <= action_last; i++)
3655 boolean found = FALSE;
3657 for (j = 0; j < NUM_DIRECTIONS; j++)
3658 if (el_act_dir2img(element, i, j) !=
3659 el_act_dir2img(element, ACTION_DEFAULT, j))
3663 num_special_action++;
3668 return num_special_action;
3673 =============================================================================
3675 -----------------------------------------------------------------------------
3676 initialize and start new game
3677 =============================================================================
3682 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3683 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3684 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3686 boolean do_fading = (game_status == GAME_MODE_MAIN);
3689 int initial_move_dir = MV_DOWN;
3691 int initial_move_dir = MV_NONE;
3695 game_status = GAME_MODE_PLAYING;
3698 InitGameControlValues();
3700 /* don't play tapes over network */
3701 network_playing = (options.network && !tape.playing);
3703 for (i = 0; i < MAX_PLAYERS; i++)
3705 struct PlayerInfo *player = &stored_player[i];
3707 player->index_nr = i;
3708 player->index_bit = (1 << i);
3709 player->element_nr = EL_PLAYER_1 + i;
3711 player->present = FALSE;
3712 player->active = FALSE;
3713 player->mapped = FALSE;
3715 player->killed = FALSE;
3716 player->reanimated = FALSE;
3719 player->effective_action = 0;
3720 player->programmed_action = 0;
3723 player->score_final = 0;
3725 player->gems_still_needed = level.gems_needed;
3726 player->sokobanfields_still_needed = 0;
3727 player->lights_still_needed = 0;
3728 player->friends_still_needed = 0;
3730 for (j = 0; j < MAX_NUM_KEYS; j++)
3731 player->key[j] = FALSE;
3733 player->num_white_keys = 0;
3735 player->dynabomb_count = 0;
3736 player->dynabomb_size = 1;
3737 player->dynabombs_left = 0;
3738 player->dynabomb_xl = FALSE;
3740 player->MovDir = initial_move_dir;
3743 player->GfxDir = initial_move_dir;
3744 player->GfxAction = ACTION_DEFAULT;
3746 player->StepFrame = 0;
3748 player->initial_element = player->element_nr;
3749 player->artwork_element =
3750 (level.use_artwork_element[i] ? level.artwork_element[i] :
3751 player->element_nr);
3752 player->use_murphy = FALSE;
3754 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3755 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3757 player->gravity = level.initial_player_gravity[i];
3759 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3761 player->actual_frame_counter = 0;
3763 player->step_counter = 0;
3765 player->last_move_dir = initial_move_dir;
3767 player->is_active = FALSE;
3769 player->is_waiting = FALSE;
3770 player->is_moving = FALSE;
3771 player->is_auto_moving = FALSE;
3772 player->is_digging = FALSE;
3773 player->is_snapping = FALSE;
3774 player->is_collecting = FALSE;
3775 player->is_pushing = FALSE;
3776 player->is_switching = FALSE;
3777 player->is_dropping = FALSE;
3778 player->is_dropping_pressed = FALSE;
3780 player->is_bored = FALSE;
3781 player->is_sleeping = FALSE;
3783 player->frame_counter_bored = -1;
3784 player->frame_counter_sleeping = -1;
3786 player->anim_delay_counter = 0;
3787 player->post_delay_counter = 0;
3789 player->dir_waiting = initial_move_dir;
3790 player->action_waiting = ACTION_DEFAULT;
3791 player->last_action_waiting = ACTION_DEFAULT;
3792 player->special_action_bored = ACTION_DEFAULT;
3793 player->special_action_sleeping = ACTION_DEFAULT;
3795 player->switch_x = -1;
3796 player->switch_y = -1;
3798 player->drop_x = -1;
3799 player->drop_y = -1;
3801 player->show_envelope = 0;
3803 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3805 player->push_delay = -1; /* initialized when pushing starts */
3806 player->push_delay_value = game.initial_push_delay_value;
3808 player->drop_delay = 0;
3809 player->drop_pressed_delay = 0;
3811 player->last_jx = -1;
3812 player->last_jy = -1;
3816 player->shield_normal_time_left = 0;
3817 player->shield_deadly_time_left = 0;
3819 player->inventory_infinite_element = EL_UNDEFINED;
3820 player->inventory_size = 0;
3822 if (level.use_initial_inventory[i])
3824 for (j = 0; j < level.initial_inventory_size[i]; j++)
3826 int element = level.initial_inventory_content[i][j];
3827 int collect_count = element_info[element].collect_count_initial;
3830 if (!IS_CUSTOM_ELEMENT(element))
3833 if (collect_count == 0)
3834 player->inventory_infinite_element = element;
3836 for (k = 0; k < collect_count; k++)
3837 if (player->inventory_size < MAX_INVENTORY_SIZE)
3838 player->inventory_element[player->inventory_size++] = element;
3842 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843 SnapField(player, 0, 0);
3845 player->LevelSolved = FALSE;
3846 player->GameOver = FALSE;
3848 player->LevelSolved_GameWon = FALSE;
3849 player->LevelSolved_GameEnd = FALSE;
3850 player->LevelSolved_PanelOff = FALSE;
3851 player->LevelSolved_SaveTape = FALSE;
3852 player->LevelSolved_SaveScore = FALSE;
3853 player->LevelSolved_CountingTime = 0;
3854 player->LevelSolved_CountingScore = 0;
3856 map_player_action[i] = i;
3859 network_player_action_received = FALSE;
3861 #if defined(NETWORK_AVALIABLE)
3862 /* initial null action */
3863 if (network_playing)
3864 SendToServer_MovePlayer(MV_NONE);
3873 TimeLeft = level.time;
3876 ScreenMovDir = MV_NONE;
3880 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3882 AllPlayersGone = FALSE;
3884 game.yamyam_content_nr = 0;
3885 game.robot_wheel_active = FALSE;
3886 game.magic_wall_active = FALSE;
3887 game.magic_wall_time_left = 0;
3888 game.light_time_left = 0;
3889 game.timegate_time_left = 0;
3890 game.switchgate_pos = 0;
3891 game.wind_direction = level.wind_direction_initial;
3893 #if !USE_PLAYER_GRAVITY
3894 game.gravity = FALSE;
3895 game.explosions_delayed = TRUE;
3898 game.lenses_time_left = 0;
3899 game.magnify_time_left = 0;
3901 game.ball_state = level.ball_state_initial;
3902 game.ball_content_nr = 0;
3904 game.envelope_active = FALSE;
3906 /* set focus to local player for network games, else to all players */
3907 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908 game.centered_player_nr_next = game.centered_player_nr;
3909 game.set_centered_player = FALSE;
3911 if (network_playing && tape.recording)
3913 /* store client dependent player focus when recording network games */
3914 tape.centered_player_nr_next = game.centered_player_nr_next;
3915 tape.set_centered_player = TRUE;
3918 for (i = 0; i < NUM_BELTS; i++)
3920 game.belt_dir[i] = MV_NONE;
3921 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3924 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3927 SCAN_PLAYFIELD(x, y)
3929 Feld[x][y] = level.field[x][y];
3930 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931 ChangeDelay[x][y] = 0;
3932 ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934 CustomValue[x][y] = 0; /* initialized in InitField() */
3936 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3938 WasJustMoving[x][y] = 0;
3939 WasJustFalling[x][y] = 0;
3940 CheckCollision[x][y] = 0;
3941 CheckImpact[x][y] = 0;
3943 Pushed[x][y] = FALSE;
3945 ChangeCount[x][y] = 0;
3946 ChangeEvent[x][y] = -1;
3948 ExplodePhase[x][y] = 0;
3949 ExplodeDelay[x][y] = 0;
3950 ExplodeField[x][y] = EX_TYPE_NONE;
3952 RunnerVisit[x][y] = 0;
3953 PlayerVisit[x][y] = 0;
3956 GfxRandom[x][y] = INIT_GFX_RANDOM();
3957 GfxElement[x][y] = EL_UNDEFINED;
3958 GfxAction[x][y] = ACTION_DEFAULT;
3959 GfxDir[x][y] = MV_NONE;
3960 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3963 SCAN_PLAYFIELD(x, y)
3965 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3967 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3969 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3972 InitField(x, y, TRUE);
3974 ResetGfxAnimation(x, y);
3979 for (i = 0; i < MAX_PLAYERS; i++)
3981 struct PlayerInfo *player = &stored_player[i];
3983 /* set number of special actions for bored and sleeping animation */
3984 player->num_special_action_bored =
3985 get_num_special_action(player->artwork_element,
3986 ACTION_BORING_1, ACTION_BORING_LAST);
3987 player->num_special_action_sleeping =
3988 get_num_special_action(player->artwork_element,
3989 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3992 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993 emulate_sb ? EMU_SOKOBAN :
3994 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3996 #if USE_NEW_ALL_SLIPPERY
3997 /* initialize type of slippery elements */
3998 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4000 if (!IS_CUSTOM_ELEMENT(i))
4002 /* default: elements slip down either to the left or right randomly */
4003 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4005 /* SP style elements prefer to slip down on the left side */
4006 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4009 /* BD style elements prefer to slip down on the left side */
4010 if (game.emulation == EMU_BOULDERDASH)
4011 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4016 /* initialize explosion and ignition delay */
4017 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4019 if (!IS_CUSTOM_ELEMENT(i))
4022 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025 int last_phase = (num_phase + 1) * delay;
4026 int half_phase = (num_phase / 2) * delay;
4028 element_info[i].explosion_delay = last_phase - 1;
4029 element_info[i].ignition_delay = half_phase;
4031 if (i == EL_BLACK_ORB)
4032 element_info[i].ignition_delay = 1;
4036 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4037 element_info[i].explosion_delay = 1;
4039 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4040 element_info[i].ignition_delay = 1;
4044 /* correct non-moving belts to start moving left */
4045 for (i = 0; i < NUM_BELTS; i++)
4046 if (game.belt_dir[i] == MV_NONE)
4047 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051 /* choose default local player */
4052 local_player = &stored_player[0];
4054 for (i = 0; i < MAX_PLAYERS; i++)
4055 stored_player[i].connected = FALSE;
4057 local_player->connected = TRUE;
4058 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4062 /* try to guess locally connected team mode players (needed for correct
4063 assignment of player figures from level to locally playing players) */
4065 for (i = 0; i < MAX_PLAYERS; i++)
4066 if (tape.player_participates[i])
4067 stored_player[i].connected = TRUE;
4069 else if (setup.team_mode && !options.network)
4071 /* try to guess locally connected team mode players (needed for correct
4072 assignment of player figures from level to locally playing players) */
4074 for (i = 0; i < MAX_PLAYERS; i++)
4075 if (setup.input[i].use_joystick ||
4076 setup.input[i].key.left != KSYM_UNDEFINED)
4077 stored_player[i].connected = TRUE;
4081 for (i = 0; i < MAX_PLAYERS; i++)
4082 printf("::: player %d: %s\n", i,
4083 (stored_player[i].connected ? "connected" : "not connected"));
4085 for (i = 0; i < MAX_PLAYERS; i++)
4086 printf("::: player %d: %s\n", i,
4087 (stored_player[i].present ? "present" : "not present"));
4090 /* check if any connected player was not found in playfield */
4091 for (i = 0; i < MAX_PLAYERS; i++)
4093 struct PlayerInfo *player = &stored_player[i];
4095 if (player->connected && !player->present)
4097 struct PlayerInfo *field_player = NULL;
4100 printf("::: looking for field player for player %d ...\n", i);
4103 /* assign first free player found that is present in the playfield */
4105 /* first try: look for unmapped playfield player that is not connected */
4106 if (field_player == NULL)
4107 for (j = 0; j < MAX_PLAYERS; j++)
4108 if (stored_player[j].present &&
4109 !stored_player[j].mapped &&
4110 !stored_player[j].connected)
4111 field_player = &stored_player[j];
4113 /* second try: look for *any* unmapped playfield player */
4114 if (field_player == NULL)
4115 for (j = 0; j < MAX_PLAYERS; j++)
4116 if (stored_player[j].present &&
4117 !stored_player[j].mapped)
4118 field_player = &stored_player[j];
4120 if (field_player != NULL)
4122 int jx = field_player->jx, jy = field_player->jy;
4125 printf("::: found player figure %d\n", field_player->index_nr);
4128 player->present = FALSE;
4129 player->active = FALSE;
4131 field_player->present = TRUE;
4132 field_player->active = TRUE;
4135 player->initial_element = field_player->initial_element;
4136 player->artwork_element = field_player->artwork_element;
4138 player->block_last_field = field_player->block_last_field;
4139 player->block_delay_adjustment = field_player->block_delay_adjustment;
4142 StorePlayer[jx][jy] = field_player->element_nr;
4144 field_player->jx = field_player->last_jx = jx;
4145 field_player->jy = field_player->last_jy = jy;
4147 if (local_player == player)
4148 local_player = field_player;
4150 map_player_action[field_player->index_nr] = i;
4152 field_player->mapped = TRUE;
4155 printf("::: map_player_action[%d] == %d\n",
4156 field_player->index_nr, i);
4161 if (player->connected && player->present)
4162 player->mapped = TRUE;
4167 /* check if any connected player was not found in playfield */
4168 for (i = 0; i < MAX_PLAYERS; i++)
4170 struct PlayerInfo *player = &stored_player[i];
4172 if (player->connected && !player->present)
4174 for (j = 0; j < MAX_PLAYERS; j++)
4176 struct PlayerInfo *field_player = &stored_player[j];
4177 int jx = field_player->jx, jy = field_player->jy;
4179 /* assign first free player found that is present in the playfield */
4180 if (field_player->present && !field_player->connected)
4182 player->present = TRUE;
4183 player->active = TRUE;
4185 field_player->present = FALSE;
4186 field_player->active = FALSE;
4188 player->initial_element = field_player->initial_element;
4189 player->artwork_element = field_player->artwork_element;
4191 player->block_last_field = field_player->block_last_field;
4192 player->block_delay_adjustment = field_player->block_delay_adjustment;
4194 StorePlayer[jx][jy] = player->element_nr;
4196 player->jx = player->last_jx = jx;
4197 player->jy = player->last_jy = jy;
4207 printf("::: local_player->present == %d\n", local_player->present);
4212 /* when playing a tape, eliminate all players who do not participate */
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215 for (i = 0; i < MAX_PLAYERS; i++)
4217 if (stored_player[i].active &&
4218 !tape.player_participates[map_player_action[i]])
4220 struct PlayerInfo *player = &stored_player[i];
4221 int jx = player->jx, jy = player->jy;
4223 player->active = FALSE;
4224 StorePlayer[jx][jy] = 0;
4225 Feld[jx][jy] = EL_EMPTY;
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 if (stored_player[i].active &&
4232 !tape.player_participates[i])
4234 struct PlayerInfo *player = &stored_player[i];
4235 int jx = player->jx, jy = player->jy;
4237 player->active = FALSE;
4238 StorePlayer[jx][jy] = 0;
4239 Feld[jx][jy] = EL_EMPTY;
4244 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4246 /* when in single player mode, eliminate all but the first active player */
4248 for (i = 0; i < MAX_PLAYERS; i++)
4250 if (stored_player[i].active)
4252 for (j = i + 1; j < MAX_PLAYERS; j++)
4254 if (stored_player[j].active)
4256 struct PlayerInfo *player = &stored_player[j];
4257 int jx = player->jx, jy = player->jy;
4259 player->active = FALSE;
4260 player->present = FALSE;
4262 StorePlayer[jx][jy] = 0;
4263 Feld[jx][jy] = EL_EMPTY;
4270 /* when recording the game, store which players take part in the game */
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274 for (i = 0; i < MAX_PLAYERS; i++)
4275 if (stored_player[i].connected)
4276 tape.player_participates[i] = TRUE;
4278 for (i = 0; i < MAX_PLAYERS; i++)
4279 if (stored_player[i].active)
4280 tape.player_participates[i] = TRUE;
4286 for (i = 0; i < MAX_PLAYERS; i++)
4288 struct PlayerInfo *player = &stored_player[i];
4290 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4295 if (local_player == player)
4296 printf("Player %d is local player.\n", i+1);
4300 if (BorderElement == EL_EMPTY)
4303 SBX_Right = lev_fieldx - SCR_FIELDX;
4305 SBY_Lower = lev_fieldy - SCR_FIELDY;
4310 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4312 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4315 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4318 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4321 /* if local player not found, look for custom element that might create
4322 the player (make some assumptions about the right custom element) */
4323 if (!local_player->present)
4325 int start_x = 0, start_y = 0;
4326 int found_rating = 0;
4327 int found_element = EL_UNDEFINED;
4328 int player_nr = local_player->index_nr;
4330 SCAN_PLAYFIELD(x, y)
4332 int element = Feld[x][y];
4337 if (level.use_start_element[player_nr] &&
4338 level.start_element[player_nr] == element &&
4345 found_element = element;
4348 if (!IS_CUSTOM_ELEMENT(element))
4351 if (CAN_CHANGE(element))
4353 for (i = 0; i < element_info[element].num_change_pages; i++)
4355 /* check for player created from custom element as single target */
4356 content = element_info[element].change_page[i].target_element;
4357 is_player = ELEM_IS_PLAYER(content);
4359 if (is_player && (found_rating < 3 ||
4360 (found_rating == 3 && element < found_element)))
4366 found_element = element;
4371 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4373 /* check for player created from custom element as explosion content */
4374 content = element_info[element].content.e[xx][yy];
4375 is_player = ELEM_IS_PLAYER(content);
4377 if (is_player && (found_rating < 2 ||
4378 (found_rating == 2 && element < found_element)))
4380 start_x = x + xx - 1;
4381 start_y = y + yy - 1;
4384 found_element = element;
4387 if (!CAN_CHANGE(element))
4390 for (i = 0; i < element_info[element].num_change_pages; i++)
4392 /* check for player created from custom element as extended target */
4394 element_info[element].change_page[i].target_content.e[xx][yy];
4396 is_player = ELEM_IS_PLAYER(content);
4398 if (is_player && (found_rating < 1 ||
4399 (found_rating == 1 && element < found_element)))
4401 start_x = x + xx - 1;
4402 start_y = y + yy - 1;
4405 found_element = element;
4411 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4412 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4415 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4421 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4422 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423 local_player->jx - MIDPOSX);
4425 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427 local_player->jy - MIDPOSY);
4431 /* do not use PLAYING mask for fading out from main screen */
4432 game_status = GAME_MODE_MAIN;
4437 if (!game.restart_level)
4438 CloseDoor(DOOR_CLOSE_1);
4441 if (level_editor_test_game)
4442 FadeSkipNextFadeIn();
4444 FadeSetEnterScreen();
4446 if (level_editor_test_game)
4447 fading = fading_none;
4449 fading = menu.destination;
4453 FadeOut(REDRAW_FIELD);
4456 FadeOut(REDRAW_FIELD);
4460 game_status = GAME_MODE_PLAYING;
4463 /* !!! FIX THIS (START) !!! */
4464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4466 InitGameEngine_EM();
4468 /* blit playfield from scroll buffer to normal back buffer for fading in */
4469 BlitScreenToBitmap_EM(backbuffer);
4471 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4473 InitGameEngine_SP();
4475 /* blit playfield from scroll buffer to normal back buffer for fading in */
4476 BlitScreenToBitmap_SP(backbuffer);
4483 /* after drawing the level, correct some elements */
4484 if (game.timegate_time_left == 0)
4485 CloseAllOpenTimegates();
4487 /* blit playfield from scroll buffer to normal back buffer for fading in */
4488 if (setup.soft_scrolling)
4489 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4491 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4493 /* !!! FIX THIS (END) !!! */
4496 FadeIn(REDRAW_FIELD);
4499 FadeIn(REDRAW_FIELD);
4504 if (!game.restart_level)
4506 /* copy default game door content to main double buffer */
4507 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4508 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4511 SetPanelBackground();
4512 SetDrawBackgroundMask(REDRAW_DOOR_1);
4515 UpdateAndDisplayGameControlValues();
4517 UpdateGameDoorValues();
4518 DrawGameDoorValues();
4521 if (!game.restart_level)
4525 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4526 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4527 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4531 /* copy actual game door content to door double buffer for OpenDoor() */
4532 BlitBitmap(drawto, bitmap_db_door,
4533 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4535 OpenDoor(DOOR_OPEN_ALL);
4537 PlaySound(SND_GAME_STARTING);
4539 if (setup.sound_music)
4542 KeyboardAutoRepeatOffUnlessAutoplay();
4546 for (i = 0; i < MAX_PLAYERS; i++)
4547 printf("Player %d %sactive.\n",
4548 i + 1, (stored_player[i].active ? "" : "not "));
4559 game.restart_level = FALSE;
4562 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4564 /* this is used for non-R'n'D game engines to update certain engine values */
4566 /* needed to determine if sounds are played within the visible screen area */
4567 scroll_x = actual_scroll_x;
4568 scroll_y = actual_scroll_y;
4571 void InitMovDir(int x, int y)
4573 int i, element = Feld[x][y];
4574 static int xy[4][2] =
4581 static int direction[3][4] =
4583 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4584 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4585 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4594 Feld[x][y] = EL_BUG;
4595 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4598 case EL_SPACESHIP_RIGHT:
4599 case EL_SPACESHIP_UP:
4600 case EL_SPACESHIP_LEFT:
4601 case EL_SPACESHIP_DOWN:
4602 Feld[x][y] = EL_SPACESHIP;
4603 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4606 case EL_BD_BUTTERFLY_RIGHT:
4607 case EL_BD_BUTTERFLY_UP:
4608 case EL_BD_BUTTERFLY_LEFT:
4609 case EL_BD_BUTTERFLY_DOWN:
4610 Feld[x][y] = EL_BD_BUTTERFLY;
4611 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4614 case EL_BD_FIREFLY_RIGHT:
4615 case EL_BD_FIREFLY_UP:
4616 case EL_BD_FIREFLY_LEFT:
4617 case EL_BD_FIREFLY_DOWN:
4618 Feld[x][y] = EL_BD_FIREFLY;
4619 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4622 case EL_PACMAN_RIGHT:
4624 case EL_PACMAN_LEFT:
4625 case EL_PACMAN_DOWN:
4626 Feld[x][y] = EL_PACMAN;
4627 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4630 case EL_YAMYAM_LEFT:
4631 case EL_YAMYAM_RIGHT:
4633 case EL_YAMYAM_DOWN:
4634 Feld[x][y] = EL_YAMYAM;
4635 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4638 case EL_SP_SNIKSNAK:
4639 MovDir[x][y] = MV_UP;
4642 case EL_SP_ELECTRON:
4643 MovDir[x][y] = MV_LEFT;
4650 Feld[x][y] = EL_MOLE;
4651 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4655 if (IS_CUSTOM_ELEMENT(element))
4657 struct ElementInfo *ei = &element_info[element];
4658 int move_direction_initial = ei->move_direction_initial;
4659 int move_pattern = ei->move_pattern;
4661 if (move_direction_initial == MV_START_PREVIOUS)
4663 if (MovDir[x][y] != MV_NONE)
4666 move_direction_initial = MV_START_AUTOMATIC;
4669 if (move_direction_initial == MV_START_RANDOM)
4670 MovDir[x][y] = 1 << RND(4);
4671 else if (move_direction_initial & MV_ANY_DIRECTION)
4672 MovDir[x][y] = move_direction_initial;
4673 else if (move_pattern == MV_ALL_DIRECTIONS ||
4674 move_pattern == MV_TURNING_LEFT ||
4675 move_pattern == MV_TURNING_RIGHT ||
4676 move_pattern == MV_TURNING_LEFT_RIGHT ||
4677 move_pattern == MV_TURNING_RIGHT_LEFT ||
4678 move_pattern == MV_TURNING_RANDOM)
4679 MovDir[x][y] = 1 << RND(4);
4680 else if (move_pattern == MV_HORIZONTAL)
4681 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4682 else if (move_pattern == MV_VERTICAL)
4683 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4684 else if (move_pattern & MV_ANY_DIRECTION)
4685 MovDir[x][y] = element_info[element].move_pattern;
4686 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4687 move_pattern == MV_ALONG_RIGHT_SIDE)
4689 /* use random direction as default start direction */
4690 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4691 MovDir[x][y] = 1 << RND(4);
4693 for (i = 0; i < NUM_DIRECTIONS; i++)
4695 int x1 = x + xy[i][0];
4696 int y1 = y + xy[i][1];
4698 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4700 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4701 MovDir[x][y] = direction[0][i];
4703 MovDir[x][y] = direction[1][i];
4712 MovDir[x][y] = 1 << RND(4);
4714 if (element != EL_BUG &&
4715 element != EL_SPACESHIP &&
4716 element != EL_BD_BUTTERFLY &&
4717 element != EL_BD_FIREFLY)
4720 for (i = 0; i < NUM_DIRECTIONS; i++)
4722 int x1 = x + xy[i][0];
4723 int y1 = y + xy[i][1];
4725 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4727 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4729 MovDir[x][y] = direction[0][i];
4732 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4733 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4735 MovDir[x][y] = direction[1][i];
4744 GfxDir[x][y] = MovDir[x][y];
4747 void InitAmoebaNr(int x, int y)
4750 int group_nr = AmoebeNachbarNr(x, y);
4754 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4756 if (AmoebaCnt[i] == 0)
4764 AmoebaNr[x][y] = group_nr;
4765 AmoebaCnt[group_nr]++;
4766 AmoebaCnt2[group_nr]++;
4769 static void PlayerWins(struct PlayerInfo *player)
4771 player->LevelSolved = TRUE;
4772 player->GameOver = TRUE;
4774 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4775 level.native_em_level->lev->score : player->score);
4777 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4778 player->LevelSolved_CountingScore = player->score_final;
4783 static int time, time_final;
4784 static int score, score_final;
4785 static int game_over_delay_1 = 0;
4786 static int game_over_delay_2 = 0;
4787 int game_over_delay_value_1 = 50;
4788 int game_over_delay_value_2 = 50;
4790 if (!local_player->LevelSolved_GameWon)
4794 /* do not start end game actions before the player stops moving (to exit) */
4795 if (local_player->MovPos)
4798 local_player->LevelSolved_GameWon = TRUE;
4799 local_player->LevelSolved_SaveTape = tape.recording;
4800 local_player->LevelSolved_SaveScore = !tape.playing;
4802 if (tape.auto_play) /* tape might already be stopped here */
4803 tape.auto_play_level_solved = TRUE;
4809 game_over_delay_1 = game_over_delay_value_1;
4810 game_over_delay_2 = game_over_delay_value_2;
4812 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4813 score = score_final = local_player->score_final;
4818 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4820 else if (level.time == 0 && TimePlayed < 999)
4823 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4826 local_player->score_final = score_final;
4828 if (level_editor_test_game)
4831 score = score_final;
4834 local_player->LevelSolved_CountingTime = time;
4835 local_player->LevelSolved_CountingScore = score;
4837 game_panel_controls[GAME_PANEL_TIME].value = time;
4838 game_panel_controls[GAME_PANEL_SCORE].value = score;
4840 DisplayGameControlValues();
4842 DrawGameValue_Time(time);
4843 DrawGameValue_Score(score);
4847 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4849 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4851 /* close exit door after last player */
4852 if ((AllPlayersGone &&
4853 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4854 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4855 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4856 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4857 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4859 int element = Feld[ExitX][ExitY];
4862 if (element == EL_EM_EXIT_OPEN ||
4863 element == EL_EM_STEEL_EXIT_OPEN)
4870 Feld[ExitX][ExitY] =
4871 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4872 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4873 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4874 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4875 EL_EM_STEEL_EXIT_CLOSING);
4877 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4881 /* player disappears */
4882 DrawLevelField(ExitX, ExitY);
4885 for (i = 0; i < MAX_PLAYERS; i++)
4887 struct PlayerInfo *player = &stored_player[i];
4889 if (player->present)
4891 RemovePlayer(player);
4893 /* player disappears */
4894 DrawLevelField(player->jx, player->jy);
4899 PlaySound(SND_GAME_WINNING);
4902 if (game_over_delay_1 > 0)
4904 game_over_delay_1--;
4909 if (time != time_final)
4911 int time_to_go = ABS(time_final - time);
4912 int time_count_dir = (time < time_final ? +1 : -1);
4913 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4915 time += time_count_steps * time_count_dir;
4916 score += time_count_steps * level.score[SC_TIME_BONUS];
4919 local_player->LevelSolved_CountingTime = time;
4920 local_player->LevelSolved_CountingScore = score;
4922 game_panel_controls[GAME_PANEL_TIME].value = time;
4923 game_panel_controls[GAME_PANEL_SCORE].value = score;
4925 DisplayGameControlValues();
4927 DrawGameValue_Time(time);
4928 DrawGameValue_Score(score);
4931 if (time == time_final)
4932 StopSound(SND_GAME_LEVELTIME_BONUS);
4933 else if (setup.sound_loops)
4934 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4936 PlaySound(SND_GAME_LEVELTIME_BONUS);
4941 local_player->LevelSolved_PanelOff = TRUE;
4943 if (game_over_delay_2 > 0)
4945 game_over_delay_2--;
4958 boolean raise_level = FALSE;
4960 local_player->LevelSolved_GameEnd = TRUE;
4962 CloseDoor(DOOR_CLOSE_1);
4964 if (local_player->LevelSolved_SaveTape)
4971 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4973 SaveTape(tape.level_nr); /* ask to save tape */
4977 if (level_editor_test_game)
4979 game_status = GAME_MODE_MAIN;
4982 DrawAndFadeInMainMenu(REDRAW_FIELD);
4990 if (!local_player->LevelSolved_SaveScore)
4993 FadeOut(REDRAW_FIELD);
4996 game_status = GAME_MODE_MAIN;
4998 DrawAndFadeInMainMenu(REDRAW_FIELD);
5003 if (level_nr == leveldir_current->handicap_level)
5005 leveldir_current->handicap_level++;
5006 SaveLevelSetup_SeriesInfo();
5009 if (level_nr < leveldir_current->last_level)
5010 raise_level = TRUE; /* advance to next level */
5012 if ((hi_pos = NewHiScore()) >= 0)
5014 game_status = GAME_MODE_SCORES;
5016 DrawHallOfFame(hi_pos);
5027 FadeOut(REDRAW_FIELD);
5030 game_status = GAME_MODE_MAIN;
5038 DrawAndFadeInMainMenu(REDRAW_FIELD);
5047 LoadScore(level_nr);
5049 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5053 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5055 if (local_player->score_final > highscore[k].Score)
5057 /* player has made it to the hall of fame */
5059 if (k < MAX_SCORE_ENTRIES - 1)
5061 int m = MAX_SCORE_ENTRIES - 1;
5064 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5065 if (strEqual(setup.player_name, highscore[l].Name))
5067 if (m == k) /* player's new highscore overwrites his old one */
5071 for (l = m; l > k; l--)
5073 strcpy(highscore[l].Name, highscore[l - 1].Name);
5074 highscore[l].Score = highscore[l - 1].Score;
5081 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5082 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5083 highscore[k].Score = local_player->score_final;
5089 else if (!strncmp(setup.player_name, highscore[k].Name,
5090 MAX_PLAYER_NAME_LEN))
5091 break; /* player already there with a higher score */
5097 SaveScore(level_nr);
5102 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5104 int element = Feld[x][y];
5105 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5106 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5107 int horiz_move = (dx != 0);
5108 int sign = (horiz_move ? dx : dy);
5109 int step = sign * element_info[element].move_stepsize;
5111 /* special values for move stepsize for spring and things on conveyor belt */
5114 if (CAN_FALL(element) &&
5115 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5116 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5117 else if (element == EL_SPRING)
5118 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5124 inline static int getElementMoveStepsize(int x, int y)
5126 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5129 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5131 if (player->GfxAction != action || player->GfxDir != dir)
5134 printf("Player frame reset! (%d => %d, %d => %d)\n",
5135 player->GfxAction, action, player->GfxDir, dir);
5138 player->GfxAction = action;
5139 player->GfxDir = dir;
5141 player->StepFrame = 0;
5145 #if USE_GFX_RESET_GFX_ANIMATION
5146 static void ResetGfxFrame(int x, int y, boolean redraw)
5148 int element = Feld[x][y];
5149 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5150 int last_gfx_frame = GfxFrame[x][y];
5152 if (graphic_info[graphic].anim_global_sync)
5153 GfxFrame[x][y] = FrameCounter;
5154 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5155 GfxFrame[x][y] = CustomValue[x][y];
5156 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5157 GfxFrame[x][y] = element_info[element].collect_score;
5158 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5159 GfxFrame[x][y] = ChangeDelay[x][y];
5161 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5162 DrawLevelGraphicAnimation(x, y, graphic);
5166 static void ResetGfxAnimation(int x, int y)
5168 GfxAction[x][y] = ACTION_DEFAULT;
5169 GfxDir[x][y] = MovDir[x][y];
5172 #if USE_GFX_RESET_GFX_ANIMATION
5173 ResetGfxFrame(x, y, FALSE);
5177 static void ResetRandomAnimationValue(int x, int y)
5179 GfxRandom[x][y] = INIT_GFX_RANDOM();
5182 void InitMovingField(int x, int y, int direction)
5184 int element = Feld[x][y];
5185 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5186 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5189 boolean is_moving_before, is_moving_after;
5191 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5194 /* check if element was/is moving or being moved before/after mode change */
5197 is_moving_before = (WasJustMoving[x][y] != 0);
5199 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5200 is_moving_before = WasJustMoving[x][y];
5203 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5205 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5207 /* reset animation only for moving elements which change direction of moving
5208 or which just started or stopped moving
5209 (else CEs with property "can move" / "not moving" are reset each frame) */
5210 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5212 if (is_moving_before != is_moving_after ||
5213 direction != MovDir[x][y])
5214 ResetGfxAnimation(x, y);
5216 if ((is_moving_before || is_moving_after) && !continues_moving)
5217 ResetGfxAnimation(x, y);
5220 if (!continues_moving)
5221 ResetGfxAnimation(x, y);
5224 MovDir[x][y] = direction;
5225 GfxDir[x][y] = direction;
5227 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5228 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5229 direction == MV_DOWN && CAN_FALL(element) ?
5230 ACTION_FALLING : ACTION_MOVING);
5232 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5233 ACTION_FALLING : ACTION_MOVING);
5236 /* this is needed for CEs with property "can move" / "not moving" */
5238 if (is_moving_after)
5240 if (Feld[newx][newy] == EL_EMPTY)
5241 Feld[newx][newy] = EL_BLOCKED;
5243 MovDir[newx][newy] = MovDir[x][y];
5245 #if USE_NEW_CUSTOM_VALUE
5246 CustomValue[newx][newy] = CustomValue[x][y];
5249 GfxFrame[newx][newy] = GfxFrame[x][y];
5250 GfxRandom[newx][newy] = GfxRandom[x][y];
5251 GfxAction[newx][newy] = GfxAction[x][y];
5252 GfxDir[newx][newy] = GfxDir[x][y];
5256 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5258 int direction = MovDir[x][y];
5259 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5260 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5266 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5268 int oldx = x, oldy = y;
5269 int direction = MovDir[x][y];
5271 if (direction == MV_LEFT)
5273 else if (direction == MV_RIGHT)
5275 else if (direction == MV_UP)
5277 else if (direction == MV_DOWN)
5280 *comes_from_x = oldx;
5281 *comes_from_y = oldy;
5284 int MovingOrBlocked2Element(int x, int y)
5286 int element = Feld[x][y];
5288 if (element == EL_BLOCKED)
5292 Blocked2Moving(x, y, &oldx, &oldy);
5293 return Feld[oldx][oldy];
5299 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5301 /* like MovingOrBlocked2Element(), but if element is moving
5302 and (x,y) is the field the moving element is just leaving,
5303 return EL_BLOCKED instead of the element value */
5304 int element = Feld[x][y];
5306 if (IS_MOVING(x, y))
5308 if (element == EL_BLOCKED)
5312 Blocked2Moving(x, y, &oldx, &oldy);
5313 return Feld[oldx][oldy];
5322 static void RemoveField(int x, int y)
5324 Feld[x][y] = EL_EMPTY;
5330 #if USE_NEW_CUSTOM_VALUE
5331 CustomValue[x][y] = 0;
5335 ChangeDelay[x][y] = 0;
5336 ChangePage[x][y] = -1;
5337 Pushed[x][y] = FALSE;
5340 ExplodeField[x][y] = EX_TYPE_NONE;
5343 GfxElement[x][y] = EL_UNDEFINED;
5344 GfxAction[x][y] = ACTION_DEFAULT;
5345 GfxDir[x][y] = MV_NONE;
5347 /* !!! this would prevent the removed tile from being redrawn !!! */
5348 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5352 void RemoveMovingField(int x, int y)
5354 int oldx = x, oldy = y, newx = x, newy = y;
5355 int element = Feld[x][y];
5356 int next_element = EL_UNDEFINED;
5358 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5361 if (IS_MOVING(x, y))
5363 Moving2Blocked(x, y, &newx, &newy);
5365 if (Feld[newx][newy] != EL_BLOCKED)
5367 /* element is moving, but target field is not free (blocked), but
5368 already occupied by something different (example: acid pool);
5369 in this case, only remove the moving field, but not the target */
5371 RemoveField(oldx, oldy);
5373 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5375 TEST_DrawLevelField(oldx, oldy);
5380 else if (element == EL_BLOCKED)
5382 Blocked2Moving(x, y, &oldx, &oldy);
5383 if (!IS_MOVING(oldx, oldy))
5387 if (element == EL_BLOCKED &&
5388 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5389 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5390 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5391 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5392 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5393 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5394 next_element = get_next_element(Feld[oldx][oldy]);
5396 RemoveField(oldx, oldy);
5397 RemoveField(newx, newy);
5399 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5401 if (next_element != EL_UNDEFINED)
5402 Feld[oldx][oldy] = next_element;
5404 TEST_DrawLevelField(oldx, oldy);
5405 TEST_DrawLevelField(newx, newy);
5408 void DrawDynamite(int x, int y)
5410 int sx = SCREENX(x), sy = SCREENY(y);
5411 int graphic = el2img(Feld[x][y]);
5414 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5417 if (IS_WALKABLE_INSIDE(Back[x][y]))
5421 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5422 else if (Store[x][y])
5423 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5425 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5427 if (Back[x][y] || Store[x][y])
5428 DrawGraphicThruMask(sx, sy, graphic, frame);
5430 DrawGraphic(sx, sy, graphic, frame);
5433 void CheckDynamite(int x, int y)
5435 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5439 if (MovDelay[x][y] != 0)
5442 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5448 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5453 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5455 boolean num_checked_players = 0;
5458 for (i = 0; i < MAX_PLAYERS; i++)
5460 if (stored_player[i].active)
5462 int sx = stored_player[i].jx;
5463 int sy = stored_player[i].jy;
5465 if (num_checked_players == 0)
5472 *sx1 = MIN(*sx1, sx);
5473 *sy1 = MIN(*sy1, sy);
5474 *sx2 = MAX(*sx2, sx);
5475 *sy2 = MAX(*sy2, sy);
5478 num_checked_players++;
5483 static boolean checkIfAllPlayersFitToScreen_RND()
5485 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5487 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5489 return (sx2 - sx1 < SCR_FIELDX &&
5490 sy2 - sy1 < SCR_FIELDY);
5493 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5495 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5497 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5499 *sx = (sx1 + sx2) / 2;
5500 *sy = (sy1 + sy2) / 2;
5503 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5504 boolean center_screen, boolean quick_relocation)
5506 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5507 boolean no_delay = (tape.warp_forward);
5508 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5509 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5511 if (quick_relocation)
5513 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5515 if (!level.shifted_relocation || center_screen)
5517 /* quick relocation (without scrolling), with centering of screen */
5519 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5520 x > SBX_Right + MIDPOSX ? SBX_Right :
5523 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5524 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5529 /* quick relocation (without scrolling), but do not center screen */
5531 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5532 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5535 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5536 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5539 int offset_x = x + (scroll_x - center_scroll_x);
5540 int offset_y = y + (scroll_y - center_scroll_y);
5542 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5543 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5544 offset_x - MIDPOSX);
5546 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5547 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5548 offset_y - MIDPOSY);
5554 if (!level.shifted_relocation || center_screen)
5556 /* quick relocation (without scrolling), with centering of screen */
5558 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5559 x > SBX_Right + MIDPOSX ? SBX_Right :
5562 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5563 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5568 /* quick relocation (without scrolling), but do not center screen */
5570 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5571 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5574 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5575 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5578 int offset_x = x + (scroll_x - center_scroll_x);
5579 int offset_y = y + (scroll_y - center_scroll_y);
5581 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5582 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5583 offset_x - MIDPOSX);
5585 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5586 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5587 offset_y - MIDPOSY);
5590 /* quick relocation (without scrolling), inside visible screen area */
5592 int offset = game.scroll_delay_value;
5594 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5595 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5596 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5598 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5599 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5600 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5602 /* don't scroll over playfield boundaries */
5603 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5604 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5606 /* don't scroll over playfield boundaries */
5607 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5608 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5612 RedrawPlayfield(TRUE, 0,0,0,0);
5617 int scroll_xx, scroll_yy;
5619 if (!level.shifted_relocation || center_screen)
5621 /* visible relocation (with scrolling), with centering of screen */
5623 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5624 x > SBX_Right + MIDPOSX ? SBX_Right :
5627 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5628 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5633 /* visible relocation (with scrolling), but do not center screen */
5635 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5636 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5639 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5640 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5643 int offset_x = x + (scroll_x - center_scroll_x);
5644 int offset_y = y + (scroll_y - center_scroll_y);
5646 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5647 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5648 offset_x - MIDPOSX);
5650 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5651 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5652 offset_y - MIDPOSY);
5657 /* visible relocation (with scrolling), with centering of screen */
5659 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5660 x > SBX_Right + MIDPOSX ? SBX_Right :
5663 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5664 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5668 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5670 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5673 int fx = FX, fy = FY;
5675 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5676 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5678 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5684 fx += dx * TILEX / 2;
5685 fy += dy * TILEY / 2;
5687 ScrollLevel(dx, dy);
5690 /* scroll in two steps of half tile size to make things smoother */
5691 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5693 Delay(wait_delay_value);
5695 /* scroll second step to align at full tile size */
5697 Delay(wait_delay_value);
5702 Delay(wait_delay_value);
5706 void RelocatePlayer(int jx, int jy, int el_player_raw)
5708 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5709 int player_nr = GET_PLAYER_NR(el_player);
5710 struct PlayerInfo *player = &stored_player[player_nr];
5711 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5712 boolean no_delay = (tape.warp_forward);
5713 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5714 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5715 int old_jx = player->jx;
5716 int old_jy = player->jy;
5717 int old_element = Feld[old_jx][old_jy];
5718 int element = Feld[jx][jy];
5719 boolean player_relocated = (old_jx != jx || old_jy != jy);
5721 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5722 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5723 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5724 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5725 int leave_side_horiz = move_dir_horiz;
5726 int leave_side_vert = move_dir_vert;
5727 int enter_side = enter_side_horiz | enter_side_vert;
5728 int leave_side = leave_side_horiz | leave_side_vert;
5730 if (player->GameOver) /* do not reanimate dead player */
5733 if (!player_relocated) /* no need to relocate the player */
5736 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5738 RemoveField(jx, jy); /* temporarily remove newly placed player */
5739 DrawLevelField(jx, jy);
5742 if (player->present)
5744 while (player->MovPos)
5746 ScrollPlayer(player, SCROLL_GO_ON);
5747 ScrollScreen(NULL, SCROLL_GO_ON);
5749 AdvanceFrameAndPlayerCounters(player->index_nr);
5754 Delay(wait_delay_value);
5757 DrawPlayer(player); /* needed here only to cleanup last field */
5758 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5760 player->is_moving = FALSE;
5763 if (IS_CUSTOM_ELEMENT(old_element))
5764 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5766 player->index_bit, leave_side);
5768 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5770 player->index_bit, leave_side);
5772 Feld[jx][jy] = el_player;
5773 InitPlayerField(jx, jy, el_player, TRUE);
5775 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5777 Feld[jx][jy] = element;
5778 InitField(jx, jy, FALSE);
5781 /* only visually relocate centered player */
5782 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5783 FALSE, level.instant_relocation);
5785 TestIfPlayerTouchesBadThing(jx, jy);
5786 TestIfPlayerTouchesCustomElement(jx, jy);
5788 if (IS_CUSTOM_ELEMENT(element))
5789 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5790 player->index_bit, enter_side);
5792 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5793 player->index_bit, enter_side);
5796 if (player->is_switching)
5798 /* ensure that relocation while still switching an element does not cause
5799 a new element to be treated as also switched directly after relocation
5800 (this is important for teleporter switches that teleport the player to
5801 a place where another teleporter switch is in the same direction, which
5802 would then incorrectly be treated as immediately switched before the
5803 direction key that caused the switch was released) */
5805 player->switch_x += jx - old_jx;
5806 player->switch_y += jy - old_jy;
5811 void Explode(int ex, int ey, int phase, int mode)
5817 /* !!! eliminate this variable !!! */
5818 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5820 if (game.explosions_delayed)
5822 ExplodeField[ex][ey] = mode;
5826 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5828 int center_element = Feld[ex][ey];
5829 int artwork_element, explosion_element; /* set these values later */
5832 /* --- This is only really needed (and now handled) in "Impact()". --- */
5833 /* do not explode moving elements that left the explode field in time */
5834 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5835 center_element == EL_EMPTY &&
5836 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5841 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5842 if (mode == EX_TYPE_NORMAL ||
5843 mode == EX_TYPE_CENTER ||
5844 mode == EX_TYPE_CROSS)
5845 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5848 /* remove things displayed in background while burning dynamite */
5849 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5852 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5854 /* put moving element to center field (and let it explode there) */
5855 center_element = MovingOrBlocked2Element(ex, ey);
5856 RemoveMovingField(ex, ey);
5857 Feld[ex][ey] = center_element;
5860 /* now "center_element" is finally determined -- set related values now */
5861 artwork_element = center_element; /* for custom player artwork */
5862 explosion_element = center_element; /* for custom player artwork */
5864 if (IS_PLAYER(ex, ey))
5866 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5868 artwork_element = stored_player[player_nr].artwork_element;
5870 if (level.use_explosion_element[player_nr])
5872 explosion_element = level.explosion_element[player_nr];
5873 artwork_element = explosion_element;
5878 if (mode == EX_TYPE_NORMAL ||
5879 mode == EX_TYPE_CENTER ||
5880 mode == EX_TYPE_CROSS)
5881 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5884 last_phase = element_info[explosion_element].explosion_delay + 1;
5886 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5888 int xx = x - ex + 1;
5889 int yy = y - ey + 1;
5892 if (!IN_LEV_FIELD(x, y) ||
5893 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5894 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5897 element = Feld[x][y];
5899 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5901 element = MovingOrBlocked2Element(x, y);
5903 if (!IS_EXPLOSION_PROOF(element))
5904 RemoveMovingField(x, y);
5907 /* indestructible elements can only explode in center (but not flames) */
5908 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5909 mode == EX_TYPE_BORDER)) ||
5910 element == EL_FLAMES)
5913 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5914 behaviour, for example when touching a yamyam that explodes to rocks
5915 with active deadly shield, a rock is created under the player !!! */
5916 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5918 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5919 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5920 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5922 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5925 if (IS_ACTIVE_BOMB(element))
5927 /* re-activate things under the bomb like gate or penguin */
5928 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5935 /* save walkable background elements while explosion on same tile */
5936 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5937 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5938 Back[x][y] = element;
5940 /* ignite explodable elements reached by other explosion */
5941 if (element == EL_EXPLOSION)
5942 element = Store2[x][y];
5944 if (AmoebaNr[x][y] &&
5945 (element == EL_AMOEBA_FULL ||
5946 element == EL_BD_AMOEBA ||
5947 element == EL_AMOEBA_GROWING))
5949 AmoebaCnt[AmoebaNr[x][y]]--;
5950 AmoebaCnt2[AmoebaNr[x][y]]--;
5955 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5957 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5959 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5961 if (PLAYERINFO(ex, ey)->use_murphy)
5962 Store[x][y] = EL_EMPTY;
5965 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5966 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5967 else if (ELEM_IS_PLAYER(center_element))
5968 Store[x][y] = EL_EMPTY;
5969 else if (center_element == EL_YAMYAM)
5970 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5971 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5972 Store[x][y] = element_info[center_element].content.e[xx][yy];
5974 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5975 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5976 otherwise) -- FIX THIS !!! */
5977 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5978 Store[x][y] = element_info[element].content.e[1][1];
5980 else if (!CAN_EXPLODE(element))
5981 Store[x][y] = element_info[element].content.e[1][1];
5984 Store[x][y] = EL_EMPTY;
5986 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5987 center_element == EL_AMOEBA_TO_DIAMOND)
5988 Store2[x][y] = element;
5990 Feld[x][y] = EL_EXPLOSION;
5991 GfxElement[x][y] = artwork_element;
5993 ExplodePhase[x][y] = 1;
5994 ExplodeDelay[x][y] = last_phase;
5999 if (center_element == EL_YAMYAM)
6000 game.yamyam_content_nr =
6001 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6013 GfxFrame[x][y] = 0; /* restart explosion animation */
6015 last_phase = ExplodeDelay[x][y];
6017 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6021 /* activate this even in non-DEBUG version until cause for crash in
6022 getGraphicAnimationFrame() (see below) is found and eliminated */
6028 /* this can happen if the player leaves an explosion just in time */
6029 if (GfxElement[x][y] == EL_UNDEFINED)
6030 GfxElement[x][y] = EL_EMPTY;
6032 if (GfxElement[x][y] == EL_UNDEFINED)
6035 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6036 printf("Explode(): This should never happen!\n");
6039 GfxElement[x][y] = EL_EMPTY;
6045 border_element = Store2[x][y];
6046 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6047 border_element = StorePlayer[x][y];
6049 if (phase == element_info[border_element].ignition_delay ||
6050 phase == last_phase)
6052 boolean border_explosion = FALSE;
6054 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6055 !PLAYER_EXPLOSION_PROTECTED(x, y))
6057 KillPlayerUnlessExplosionProtected(x, y);
6058 border_explosion = TRUE;
6060 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6062 Feld[x][y] = Store2[x][y];
6065 border_explosion = TRUE;
6067 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6069 AmoebeUmwandeln(x, y);
6071 border_explosion = TRUE;
6074 /* if an element just explodes due to another explosion (chain-reaction),
6075 do not immediately end the new explosion when it was the last frame of
6076 the explosion (as it would be done in the following "if"-statement!) */
6077 if (border_explosion && phase == last_phase)
6081 if (phase == last_phase)
6085 element = Feld[x][y] = Store[x][y];
6086 Store[x][y] = Store2[x][y] = 0;
6087 GfxElement[x][y] = EL_UNDEFINED;
6089 /* player can escape from explosions and might therefore be still alive */
6090 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6091 element <= EL_PLAYER_IS_EXPLODING_4)
6093 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6094 int explosion_element = EL_PLAYER_1 + player_nr;
6095 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6096 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6098 if (level.use_explosion_element[player_nr])
6099 explosion_element = level.explosion_element[player_nr];
6101 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6102 element_info[explosion_element].content.e[xx][yy]);
6105 /* restore probably existing indestructible background element */
6106 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6107 element = Feld[x][y] = Back[x][y];
6110 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6111 GfxDir[x][y] = MV_NONE;
6112 ChangeDelay[x][y] = 0;
6113 ChangePage[x][y] = -1;
6115 #if USE_NEW_CUSTOM_VALUE
6116 CustomValue[x][y] = 0;
6119 InitField_WithBug2(x, y, FALSE);
6121 TEST_DrawLevelField(x, y);
6123 TestIfElementTouchesCustomElement(x, y);
6125 if (GFX_CRUMBLED(element))
6126 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6128 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6129 StorePlayer[x][y] = 0;
6131 if (ELEM_IS_PLAYER(element))
6132 RelocatePlayer(x, y, element);
6134 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6136 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6137 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6140 TEST_DrawLevelFieldCrumbledSand(x, y);
6142 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6144 DrawLevelElement(x, y, Back[x][y]);
6145 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6147 else if (IS_WALKABLE_UNDER(Back[x][y]))
6149 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150 DrawLevelElementThruMask(x, y, Back[x][y]);
6152 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6153 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6157 void DynaExplode(int ex, int ey)
6160 int dynabomb_element = Feld[ex][ey];
6161 int dynabomb_size = 1;
6162 boolean dynabomb_xl = FALSE;
6163 struct PlayerInfo *player;
6164 static int xy[4][2] =
6172 if (IS_ACTIVE_BOMB(dynabomb_element))
6174 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6175 dynabomb_size = player->dynabomb_size;
6176 dynabomb_xl = player->dynabomb_xl;
6177 player->dynabombs_left++;
6180 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6182 for (i = 0; i < NUM_DIRECTIONS; i++)
6184 for (j = 1; j <= dynabomb_size; j++)
6186 int x = ex + j * xy[i][0];
6187 int y = ey + j * xy[i][1];
6190 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6193 element = Feld[x][y];
6195 /* do not restart explosions of fields with active bombs */
6196 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6199 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6201 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6202 !IS_DIGGABLE(element) && !dynabomb_xl)
6208 void Bang(int x, int y)
6210 int element = MovingOrBlocked2Element(x, y);
6211 int explosion_type = EX_TYPE_NORMAL;
6213 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6215 struct PlayerInfo *player = PLAYERINFO(x, y);
6217 #if USE_FIX_CE_ACTION_WITH_PLAYER
6218 element = Feld[x][y] = player->initial_element;
6220 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6221 player->element_nr);
6224 if (level.use_explosion_element[player->index_nr])
6226 int explosion_element = level.explosion_element[player->index_nr];
6228 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6229 explosion_type = EX_TYPE_CROSS;
6230 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6231 explosion_type = EX_TYPE_CENTER;
6239 case EL_BD_BUTTERFLY:
6242 case EL_DARK_YAMYAM:
6246 RaiseScoreElement(element);
6249 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6250 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6251 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6252 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6253 case EL_DYNABOMB_INCREASE_NUMBER:
6254 case EL_DYNABOMB_INCREASE_SIZE:
6255 case EL_DYNABOMB_INCREASE_POWER:
6256 explosion_type = EX_TYPE_DYNA;
6259 case EL_DC_LANDMINE:
6261 case EL_EM_EXIT_OPEN:
6262 case EL_EM_STEEL_EXIT_OPEN:
6264 explosion_type = EX_TYPE_CENTER;
6269 case EL_LAMP_ACTIVE:
6270 case EL_AMOEBA_TO_DIAMOND:
6271 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6272 explosion_type = EX_TYPE_CENTER;
6276 if (element_info[element].explosion_type == EXPLODES_CROSS)
6277 explosion_type = EX_TYPE_CROSS;
6278 else if (element_info[element].explosion_type == EXPLODES_1X1)
6279 explosion_type = EX_TYPE_CENTER;
6283 if (explosion_type == EX_TYPE_DYNA)
6286 Explode(x, y, EX_PHASE_START, explosion_type);
6288 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6291 void SplashAcid(int x, int y)
6293 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6294 (!IN_LEV_FIELD(x - 1, y - 2) ||
6295 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6296 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6298 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6299 (!IN_LEV_FIELD(x + 1, y - 2) ||
6300 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6301 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6303 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6306 static void InitBeltMovement()
6308 static int belt_base_element[4] =
6310 EL_CONVEYOR_BELT_1_LEFT,
6311 EL_CONVEYOR_BELT_2_LEFT,
6312 EL_CONVEYOR_BELT_3_LEFT,
6313 EL_CONVEYOR_BELT_4_LEFT
6315 static int belt_base_active_element[4] =
6317 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6318 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6319 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6320 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6325 /* set frame order for belt animation graphic according to belt direction */
6326 for (i = 0; i < NUM_BELTS; i++)
6330 for (j = 0; j < NUM_BELT_PARTS; j++)
6332 int element = belt_base_active_element[belt_nr] + j;
6333 int graphic_1 = el2img(element);
6334 int graphic_2 = el2panelimg(element);
6336 if (game.belt_dir[i] == MV_LEFT)
6338 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6339 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6343 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6344 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6349 SCAN_PLAYFIELD(x, y)
6351 int element = Feld[x][y];
6353 for (i = 0; i < NUM_BELTS; i++)
6355 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6357 int e_belt_nr = getBeltNrFromBeltElement(element);
6360 if (e_belt_nr == belt_nr)
6362 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6364 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6371 static void ToggleBeltSwitch(int x, int y)
6373 static int belt_base_element[4] =
6375 EL_CONVEYOR_BELT_1_LEFT,
6376 EL_CONVEYOR_BELT_2_LEFT,
6377 EL_CONVEYOR_BELT_3_LEFT,
6378 EL_CONVEYOR_BELT_4_LEFT
6380 static int belt_base_active_element[4] =
6382 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6383 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6384 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6385 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6387 static int belt_base_switch_element[4] =
6389 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6390 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6391 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6392 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6394 static int belt_move_dir[4] =
6402 int element = Feld[x][y];
6403 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6404 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6405 int belt_dir = belt_move_dir[belt_dir_nr];
6408 if (!IS_BELT_SWITCH(element))
6411 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6412 game.belt_dir[belt_nr] = belt_dir;
6414 if (belt_dir_nr == 3)
6417 /* set frame order for belt animation graphic according to belt direction */
6418 for (i = 0; i < NUM_BELT_PARTS; i++)
6420 int element = belt_base_active_element[belt_nr] + i;
6421 int graphic_1 = el2img(element);
6422 int graphic_2 = el2panelimg(element);
6424 if (belt_dir == MV_LEFT)
6426 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6427 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6431 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6432 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6436 SCAN_PLAYFIELD(xx, yy)
6438 int element = Feld[xx][yy];
6440 if (IS_BELT_SWITCH(element))
6442 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6444 if (e_belt_nr == belt_nr)
6446 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6447 TEST_DrawLevelField(xx, yy);
6450 else if (IS_BELT(element) && belt_dir != MV_NONE)
6452 int e_belt_nr = getBeltNrFromBeltElement(element);
6454 if (e_belt_nr == belt_nr)
6456 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6458 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6459 TEST_DrawLevelField(xx, yy);
6462 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6464 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6466 if (e_belt_nr == belt_nr)
6468 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6470 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6471 TEST_DrawLevelField(xx, yy);
6477 static void ToggleSwitchgateSwitch(int x, int y)
6481 game.switchgate_pos = !game.switchgate_pos;
6483 SCAN_PLAYFIELD(xx, yy)
6485 int element = Feld[xx][yy];
6487 #if !USE_BOTH_SWITCHGATE_SWITCHES
6488 if (element == EL_SWITCHGATE_SWITCH_UP ||
6489 element == EL_SWITCHGATE_SWITCH_DOWN)
6491 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6492 TEST_DrawLevelField(xx, yy);
6494 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6495 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6497 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6498 TEST_DrawLevelField(xx, yy);
6501 if (element == EL_SWITCHGATE_SWITCH_UP)
6503 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6504 TEST_DrawLevelField(xx, yy);
6506 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6508 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6509 TEST_DrawLevelField(xx, yy);
6511 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6513 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6514 TEST_DrawLevelField(xx, yy);
6516 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6518 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6519 TEST_DrawLevelField(xx, yy);
6522 else if (element == EL_SWITCHGATE_OPEN ||
6523 element == EL_SWITCHGATE_OPENING)
6525 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6527 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6529 else if (element == EL_SWITCHGATE_CLOSED ||
6530 element == EL_SWITCHGATE_CLOSING)
6532 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6534 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6539 static int getInvisibleActiveFromInvisibleElement(int element)
6541 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6542 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6543 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6547 static int getInvisibleFromInvisibleActiveElement(int element)
6549 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6550 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6551 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6555 static void RedrawAllLightSwitchesAndInvisibleElements()
6559 SCAN_PLAYFIELD(x, y)
6561 int element = Feld[x][y];
6563 if (element == EL_LIGHT_SWITCH &&
6564 game.light_time_left > 0)
6566 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6567 TEST_DrawLevelField(x, y);
6569 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6570 game.light_time_left == 0)
6572 Feld[x][y] = EL_LIGHT_SWITCH;
6573 TEST_DrawLevelField(x, y);
6575 else if (element == EL_EMC_DRIPPER &&
6576 game.light_time_left > 0)
6578 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6579 TEST_DrawLevelField(x, y);
6581 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6582 game.light_time_left == 0)
6584 Feld[x][y] = EL_EMC_DRIPPER;
6585 TEST_DrawLevelField(x, y);
6587 else if (element == EL_INVISIBLE_STEELWALL ||
6588 element == EL_INVISIBLE_WALL ||
6589 element == EL_INVISIBLE_SAND)
6591 if (game.light_time_left > 0)
6592 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6594 TEST_DrawLevelField(x, y);
6596 /* uncrumble neighbour fields, if needed */
6597 if (element == EL_INVISIBLE_SAND)
6598 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6600 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6601 element == EL_INVISIBLE_WALL_ACTIVE ||
6602 element == EL_INVISIBLE_SAND_ACTIVE)
6604 if (game.light_time_left == 0)
6605 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6607 TEST_DrawLevelField(x, y);
6609 /* re-crumble neighbour fields, if needed */
6610 if (element == EL_INVISIBLE_SAND)
6611 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6616 static void RedrawAllInvisibleElementsForLenses()
6620 SCAN_PLAYFIELD(x, y)
6622 int element = Feld[x][y];
6624 if (element == EL_EMC_DRIPPER &&
6625 game.lenses_time_left > 0)
6627 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6628 TEST_DrawLevelField(x, y);
6630 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6631 game.lenses_time_left == 0)
6633 Feld[x][y] = EL_EMC_DRIPPER;
6634 TEST_DrawLevelField(x, y);
6636 else if (element == EL_INVISIBLE_STEELWALL ||
6637 element == EL_INVISIBLE_WALL ||
6638 element == EL_INVISIBLE_SAND)
6640 if (game.lenses_time_left > 0)
6641 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6643 TEST_DrawLevelField(x, y);
6645 /* uncrumble neighbour fields, if needed */
6646 if (element == EL_INVISIBLE_SAND)
6647 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6649 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6650 element == EL_INVISIBLE_WALL_ACTIVE ||
6651 element == EL_INVISIBLE_SAND_ACTIVE)
6653 if (game.lenses_time_left == 0)
6654 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6656 TEST_DrawLevelField(x, y);
6658 /* re-crumble neighbour fields, if needed */
6659 if (element == EL_INVISIBLE_SAND)
6660 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6665 static void RedrawAllInvisibleElementsForMagnifier()
6669 SCAN_PLAYFIELD(x, y)
6671 int element = Feld[x][y];
6673 if (element == EL_EMC_FAKE_GRASS &&
6674 game.magnify_time_left > 0)
6676 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6677 TEST_DrawLevelField(x, y);
6679 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6680 game.magnify_time_left == 0)
6682 Feld[x][y] = EL_EMC_FAKE_GRASS;
6683 TEST_DrawLevelField(x, y);
6685 else if (IS_GATE_GRAY(element) &&
6686 game.magnify_time_left > 0)
6688 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6689 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6690 IS_EM_GATE_GRAY(element) ?
6691 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6692 IS_EMC_GATE_GRAY(element) ?
6693 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6694 IS_DC_GATE_GRAY(element) ?
6695 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6697 TEST_DrawLevelField(x, y);
6699 else if (IS_GATE_GRAY_ACTIVE(element) &&
6700 game.magnify_time_left == 0)
6702 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6703 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6704 IS_EM_GATE_GRAY_ACTIVE(element) ?
6705 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6706 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6707 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6708 IS_DC_GATE_GRAY_ACTIVE(element) ?
6709 EL_DC_GATE_WHITE_GRAY :
6711 TEST_DrawLevelField(x, y);
6716 static void ToggleLightSwitch(int x, int y)
6718 int element = Feld[x][y];
6720 game.light_time_left =
6721 (element == EL_LIGHT_SWITCH ?
6722 level.time_light * FRAMES_PER_SECOND : 0);
6724 RedrawAllLightSwitchesAndInvisibleElements();
6727 static void ActivateTimegateSwitch(int x, int y)
6731 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6733 SCAN_PLAYFIELD(xx, yy)
6735 int element = Feld[xx][yy];
6737 if (element == EL_TIMEGATE_CLOSED ||
6738 element == EL_TIMEGATE_CLOSING)
6740 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6741 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6745 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6747 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6748 TEST_DrawLevelField(xx, yy);
6755 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6756 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6758 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6762 void Impact(int x, int y)
6764 boolean last_line = (y == lev_fieldy - 1);
6765 boolean object_hit = FALSE;
6766 boolean impact = (last_line || object_hit);
6767 int element = Feld[x][y];
6768 int smashed = EL_STEELWALL;
6770 if (!last_line) /* check if element below was hit */
6772 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6775 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6776 MovDir[x][y + 1] != MV_DOWN ||
6777 MovPos[x][y + 1] <= TILEY / 2));
6779 /* do not smash moving elements that left the smashed field in time */
6780 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6781 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6784 #if USE_QUICKSAND_IMPACT_BUGFIX
6785 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6787 RemoveMovingField(x, y + 1);
6788 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6789 Feld[x][y + 2] = EL_ROCK;
6790 TEST_DrawLevelField(x, y + 2);
6795 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6797 RemoveMovingField(x, y + 1);
6798 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6799 Feld[x][y + 2] = EL_ROCK;
6800 TEST_DrawLevelField(x, y + 2);
6807 smashed = MovingOrBlocked2Element(x, y + 1);
6809 impact = (last_line || object_hit);
6812 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6814 SplashAcid(x, y + 1);
6818 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6819 /* only reset graphic animation if graphic really changes after impact */
6821 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6823 ResetGfxAnimation(x, y);
6824 TEST_DrawLevelField(x, y);
6827 if (impact && CAN_EXPLODE_IMPACT(element))
6832 else if (impact && element == EL_PEARL &&
6833 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6835 ResetGfxAnimation(x, y);
6837 Feld[x][y] = EL_PEARL_BREAKING;
6838 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6841 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6843 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6848 if (impact && element == EL_AMOEBA_DROP)
6850 if (object_hit && IS_PLAYER(x, y + 1))
6851 KillPlayerUnlessEnemyProtected(x, y + 1);
6852 else if (object_hit && smashed == EL_PENGUIN)
6856 Feld[x][y] = EL_AMOEBA_GROWING;
6857 Store[x][y] = EL_AMOEBA_WET;
6859 ResetRandomAnimationValue(x, y);
6864 if (object_hit) /* check which object was hit */
6866 if ((CAN_PASS_MAGIC_WALL(element) &&
6867 (smashed == EL_MAGIC_WALL ||
6868 smashed == EL_BD_MAGIC_WALL)) ||
6869 (CAN_PASS_DC_MAGIC_WALL(element) &&
6870 smashed == EL_DC_MAGIC_WALL))
6873 int activated_magic_wall =
6874 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6875 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6876 EL_DC_MAGIC_WALL_ACTIVE);
6878 /* activate magic wall / mill */
6879 SCAN_PLAYFIELD(xx, yy)
6881 if (Feld[xx][yy] == smashed)
6882 Feld[xx][yy] = activated_magic_wall;
6885 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6886 game.magic_wall_active = TRUE;
6888 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6889 SND_MAGIC_WALL_ACTIVATING :
6890 smashed == EL_BD_MAGIC_WALL ?
6891 SND_BD_MAGIC_WALL_ACTIVATING :
6892 SND_DC_MAGIC_WALL_ACTIVATING));
6895 if (IS_PLAYER(x, y + 1))
6897 if (CAN_SMASH_PLAYER(element))
6899 KillPlayerUnlessEnemyProtected(x, y + 1);
6903 else if (smashed == EL_PENGUIN)
6905 if (CAN_SMASH_PLAYER(element))
6911 else if (element == EL_BD_DIAMOND)
6913 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6919 else if (((element == EL_SP_INFOTRON ||
6920 element == EL_SP_ZONK) &&
6921 (smashed == EL_SP_SNIKSNAK ||
6922 smashed == EL_SP_ELECTRON ||
6923 smashed == EL_SP_DISK_ORANGE)) ||
6924 (element == EL_SP_INFOTRON &&
6925 smashed == EL_SP_DISK_YELLOW))
6930 else if (CAN_SMASH_EVERYTHING(element))
6932 if (IS_CLASSIC_ENEMY(smashed) ||
6933 CAN_EXPLODE_SMASHED(smashed))
6938 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6940 if (smashed == EL_LAMP ||
6941 smashed == EL_LAMP_ACTIVE)
6946 else if (smashed == EL_NUT)
6948 Feld[x][y + 1] = EL_NUT_BREAKING;
6949 PlayLevelSound(x, y, SND_NUT_BREAKING);
6950 RaiseScoreElement(EL_NUT);
6953 else if (smashed == EL_PEARL)
6955 ResetGfxAnimation(x, y);
6957 Feld[x][y + 1] = EL_PEARL_BREAKING;
6958 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6961 else if (smashed == EL_DIAMOND)
6963 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6964 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6967 else if (IS_BELT_SWITCH(smashed))
6969 ToggleBeltSwitch(x, y + 1);
6971 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6972 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6973 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6974 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6976 ToggleSwitchgateSwitch(x, y + 1);
6978 else if (smashed == EL_LIGHT_SWITCH ||
6979 smashed == EL_LIGHT_SWITCH_ACTIVE)
6981 ToggleLightSwitch(x, y + 1);
6986 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6989 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6991 CheckElementChangeBySide(x, y + 1, smashed, element,
6992 CE_SWITCHED, CH_SIDE_TOP);
6993 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6999 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7004 /* play sound of magic wall / mill */
7006 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7007 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7008 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7010 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7011 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7012 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7013 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7014 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7015 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7020 /* play sound of object that hits the ground */
7021 if (last_line || object_hit)
7022 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7025 inline static void TurnRoundExt(int x, int y)
7037 { 0, 0 }, { 0, 0 }, { 0, 0 },
7042 int left, right, back;
7046 { MV_DOWN, MV_UP, MV_RIGHT },
7047 { MV_UP, MV_DOWN, MV_LEFT },
7049 { MV_LEFT, MV_RIGHT, MV_DOWN },
7053 { MV_RIGHT, MV_LEFT, MV_UP }
7056 int element = Feld[x][y];
7057 int move_pattern = element_info[element].move_pattern;
7059 int old_move_dir = MovDir[x][y];
7060 int left_dir = turn[old_move_dir].left;
7061 int right_dir = turn[old_move_dir].right;
7062 int back_dir = turn[old_move_dir].back;
7064 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7065 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7066 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7067 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7069 int left_x = x + left_dx, left_y = y + left_dy;
7070 int right_x = x + right_dx, right_y = y + right_dy;
7071 int move_x = x + move_dx, move_y = y + move_dy;
7075 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7077 TestIfBadThingTouchesOtherBadThing(x, y);
7079 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7080 MovDir[x][y] = right_dir;
7081 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7082 MovDir[x][y] = left_dir;
7084 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7086 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7089 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7091 TestIfBadThingTouchesOtherBadThing(x, y);
7093 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7094 MovDir[x][y] = left_dir;
7095 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7096 MovDir[x][y] = right_dir;
7098 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7100 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7103 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7105 TestIfBadThingTouchesOtherBadThing(x, y);
7107 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7108 MovDir[x][y] = left_dir;
7109 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7110 MovDir[x][y] = right_dir;
7112 if (MovDir[x][y] != old_move_dir)
7115 else if (element == EL_YAMYAM)
7117 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7118 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7120 if (can_turn_left && can_turn_right)
7121 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7122 else if (can_turn_left)
7123 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7124 else if (can_turn_right)
7125 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7127 MovDir[x][y] = back_dir;
7129 MovDelay[x][y] = 16 + 16 * RND(3);
7131 else if (element == EL_DARK_YAMYAM)
7133 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7135 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7138 if (can_turn_left && can_turn_right)
7139 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7140 else if (can_turn_left)
7141 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7142 else if (can_turn_right)
7143 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7145 MovDir[x][y] = back_dir;
7147 MovDelay[x][y] = 16 + 16 * RND(3);
7149 else if (element == EL_PACMAN)
7151 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7152 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7154 if (can_turn_left && can_turn_right)
7155 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7156 else if (can_turn_left)
7157 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7158 else if (can_turn_right)
7159 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7161 MovDir[x][y] = back_dir;
7163 MovDelay[x][y] = 6 + RND(40);
7165 else if (element == EL_PIG)
7167 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7168 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7169 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7170 boolean should_turn_left, should_turn_right, should_move_on;
7172 int rnd = RND(rnd_value);
7174 should_turn_left = (can_turn_left &&
7176 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7177 y + back_dy + left_dy)));
7178 should_turn_right = (can_turn_right &&
7180 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7181 y + back_dy + right_dy)));
7182 should_move_on = (can_move_on &&
7185 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7186 y + move_dy + left_dy) ||
7187 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7188 y + move_dy + right_dy)));
7190 if (should_turn_left || should_turn_right || should_move_on)
7192 if (should_turn_left && should_turn_right && should_move_on)
7193 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7194 rnd < 2 * rnd_value / 3 ? right_dir :
7196 else if (should_turn_left && should_turn_right)
7197 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7198 else if (should_turn_left && should_move_on)
7199 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7200 else if (should_turn_right && should_move_on)
7201 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7202 else if (should_turn_left)
7203 MovDir[x][y] = left_dir;
7204 else if (should_turn_right)
7205 MovDir[x][y] = right_dir;
7206 else if (should_move_on)
7207 MovDir[x][y] = old_move_dir;
7209 else if (can_move_on && rnd > rnd_value / 8)
7210 MovDir[x][y] = old_move_dir;
7211 else if (can_turn_left && can_turn_right)
7212 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7213 else if (can_turn_left && rnd > rnd_value / 8)
7214 MovDir[x][y] = left_dir;
7215 else if (can_turn_right && rnd > rnd_value/8)
7216 MovDir[x][y] = right_dir;
7218 MovDir[x][y] = back_dir;
7220 xx = x + move_xy[MovDir[x][y]].dx;
7221 yy = y + move_xy[MovDir[x][y]].dy;
7223 if (!IN_LEV_FIELD(xx, yy) ||
7224 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7225 MovDir[x][y] = old_move_dir;
7229 else if (element == EL_DRAGON)
7231 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7232 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7233 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7235 int rnd = RND(rnd_value);
7237 if (can_move_on && rnd > rnd_value / 8)
7238 MovDir[x][y] = old_move_dir;
7239 else if (can_turn_left && can_turn_right)
7240 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7241 else if (can_turn_left && rnd > rnd_value / 8)
7242 MovDir[x][y] = left_dir;
7243 else if (can_turn_right && rnd > rnd_value / 8)
7244 MovDir[x][y] = right_dir;
7246 MovDir[x][y] = back_dir;
7248 xx = x + move_xy[MovDir[x][y]].dx;
7249 yy = y + move_xy[MovDir[x][y]].dy;
7251 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7252 MovDir[x][y] = old_move_dir;
7256 else if (element == EL_MOLE)
7258 boolean can_move_on =
7259 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7260 IS_AMOEBOID(Feld[move_x][move_y]) ||
7261 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7264 boolean can_turn_left =
7265 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7266 IS_AMOEBOID(Feld[left_x][left_y])));
7268 boolean can_turn_right =
7269 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7270 IS_AMOEBOID(Feld[right_x][right_y])));
7272 if (can_turn_left && can_turn_right)
7273 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7274 else if (can_turn_left)
7275 MovDir[x][y] = left_dir;
7277 MovDir[x][y] = right_dir;
7280 if (MovDir[x][y] != old_move_dir)
7283 else if (element == EL_BALLOON)
7285 MovDir[x][y] = game.wind_direction;
7288 else if (element == EL_SPRING)
7290 #if USE_NEW_SPRING_BUMPER
7291 if (MovDir[x][y] & MV_HORIZONTAL)
7293 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7294 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7296 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7297 ResetGfxAnimation(move_x, move_y);
7298 TEST_DrawLevelField(move_x, move_y);
7300 MovDir[x][y] = back_dir;
7302 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7303 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7304 MovDir[x][y] = MV_NONE;
7307 if (MovDir[x][y] & MV_HORIZONTAL &&
7308 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7309 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7310 MovDir[x][y] = MV_NONE;
7315 else if (element == EL_ROBOT ||
7316 element == EL_SATELLITE ||
7317 element == EL_PENGUIN ||
7318 element == EL_EMC_ANDROID)
7320 int attr_x = -1, attr_y = -1;
7331 for (i = 0; i < MAX_PLAYERS; i++)
7333 struct PlayerInfo *player = &stored_player[i];
7334 int jx = player->jx, jy = player->jy;
7336 if (!player->active)
7340 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7348 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7349 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7350 game.engine_version < VERSION_IDENT(3,1,0,0)))
7356 if (element == EL_PENGUIN)
7359 static int xy[4][2] =
7367 for (i = 0; i < NUM_DIRECTIONS; i++)
7369 int ex = x + xy[i][0];
7370 int ey = y + xy[i][1];
7372 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7373 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7374 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7375 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7384 MovDir[x][y] = MV_NONE;
7386 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7387 else if (attr_x > x)
7388 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7390 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7391 else if (attr_y > y)
7392 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7394 if (element == EL_ROBOT)
7398 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7399 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7400 Moving2Blocked(x, y, &newx, &newy);
7402 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7403 MovDelay[x][y] = 8 + 8 * !RND(3);
7405 MovDelay[x][y] = 16;
7407 else if (element == EL_PENGUIN)
7413 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7415 boolean first_horiz = RND(2);
7416 int new_move_dir = MovDir[x][y];
7419 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420 Moving2Blocked(x, y, &newx, &newy);
7422 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7426 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7427 Moving2Blocked(x, y, &newx, &newy);
7429 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7432 MovDir[x][y] = old_move_dir;
7436 else if (element == EL_SATELLITE)
7442 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7444 boolean first_horiz = RND(2);
7445 int new_move_dir = MovDir[x][y];
7448 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449 Moving2Blocked(x, y, &newx, &newy);
7451 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7455 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456 Moving2Blocked(x, y, &newx, &newy);
7458 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7461 MovDir[x][y] = old_move_dir;
7465 else if (element == EL_EMC_ANDROID)
7467 static int check_pos[16] =
7469 -1, /* 0 => (invalid) */
7470 7, /* 1 => MV_LEFT */
7471 3, /* 2 => MV_RIGHT */
7472 -1, /* 3 => (invalid) */
7474 0, /* 5 => MV_LEFT | MV_UP */
7475 2, /* 6 => MV_RIGHT | MV_UP */
7476 -1, /* 7 => (invalid) */
7477 5, /* 8 => MV_DOWN */
7478 6, /* 9 => MV_LEFT | MV_DOWN */
7479 4, /* 10 => MV_RIGHT | MV_DOWN */
7480 -1, /* 11 => (invalid) */
7481 -1, /* 12 => (invalid) */
7482 -1, /* 13 => (invalid) */
7483 -1, /* 14 => (invalid) */
7484 -1, /* 15 => (invalid) */
7492 { -1, -1, MV_LEFT | MV_UP },
7494 { +1, -1, MV_RIGHT | MV_UP },
7495 { +1, 0, MV_RIGHT },
7496 { +1, +1, MV_RIGHT | MV_DOWN },
7498 { -1, +1, MV_LEFT | MV_DOWN },
7501 int start_pos, check_order;
7502 boolean can_clone = FALSE;
7505 /* check if there is any free field around current position */
7506 for (i = 0; i < 8; i++)
7508 int newx = x + check_xy[i].dx;
7509 int newy = y + check_xy[i].dy;
7511 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7519 if (can_clone) /* randomly find an element to clone */
7523 start_pos = check_pos[RND(8)];
7524 check_order = (RND(2) ? -1 : +1);
7526 for (i = 0; i < 8; i++)
7528 int pos_raw = start_pos + i * check_order;
7529 int pos = (pos_raw + 8) % 8;
7530 int newx = x + check_xy[pos].dx;
7531 int newy = y + check_xy[pos].dy;
7533 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7535 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7536 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7538 Store[x][y] = Feld[newx][newy];
7547 if (can_clone) /* randomly find a direction to move */
7551 start_pos = check_pos[RND(8)];
7552 check_order = (RND(2) ? -1 : +1);
7554 for (i = 0; i < 8; i++)
7556 int pos_raw = start_pos + i * check_order;
7557 int pos = (pos_raw + 8) % 8;
7558 int newx = x + check_xy[pos].dx;
7559 int newy = y + check_xy[pos].dy;
7560 int new_move_dir = check_xy[pos].dir;
7562 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7564 MovDir[x][y] = new_move_dir;
7565 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7574 if (can_clone) /* cloning and moving successful */
7577 /* cannot clone -- try to move towards player */
7579 start_pos = check_pos[MovDir[x][y] & 0x0f];
7580 check_order = (RND(2) ? -1 : +1);
7582 for (i = 0; i < 3; i++)
7584 /* first check start_pos, then previous/next or (next/previous) pos */
7585 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7586 int pos = (pos_raw + 8) % 8;
7587 int newx = x + check_xy[pos].dx;
7588 int newy = y + check_xy[pos].dy;
7589 int new_move_dir = check_xy[pos].dir;
7591 if (IS_PLAYER(newx, newy))
7594 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7596 MovDir[x][y] = new_move_dir;
7597 MovDelay[x][y] = level.android_move_time * 8 + 1;
7604 else if (move_pattern == MV_TURNING_LEFT ||
7605 move_pattern == MV_TURNING_RIGHT ||
7606 move_pattern == MV_TURNING_LEFT_RIGHT ||
7607 move_pattern == MV_TURNING_RIGHT_LEFT ||
7608 move_pattern == MV_TURNING_RANDOM ||
7609 move_pattern == MV_ALL_DIRECTIONS)
7611 boolean can_turn_left =
7612 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7613 boolean can_turn_right =
7614 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7616 if (element_info[element].move_stepsize == 0) /* "not moving" */
7619 if (move_pattern == MV_TURNING_LEFT)
7620 MovDir[x][y] = left_dir;
7621 else if (move_pattern == MV_TURNING_RIGHT)
7622 MovDir[x][y] = right_dir;
7623 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7624 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7625 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7626 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7627 else if (move_pattern == MV_TURNING_RANDOM)
7628 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7629 can_turn_right && !can_turn_left ? right_dir :
7630 RND(2) ? left_dir : right_dir);
7631 else if (can_turn_left && can_turn_right)
7632 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7633 else if (can_turn_left)
7634 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7635 else if (can_turn_right)
7636 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7638 MovDir[x][y] = back_dir;
7640 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7642 else if (move_pattern == MV_HORIZONTAL ||
7643 move_pattern == MV_VERTICAL)
7645 if (move_pattern & old_move_dir)
7646 MovDir[x][y] = back_dir;
7647 else if (move_pattern == MV_HORIZONTAL)
7648 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7649 else if (move_pattern == MV_VERTICAL)
7650 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7652 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7654 else if (move_pattern & MV_ANY_DIRECTION)
7656 MovDir[x][y] = move_pattern;
7657 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7659 else if (move_pattern & MV_WIND_DIRECTION)
7661 MovDir[x][y] = game.wind_direction;
7662 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7664 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7666 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7667 MovDir[x][y] = left_dir;
7668 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7669 MovDir[x][y] = right_dir;
7671 if (MovDir[x][y] != old_move_dir)
7672 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7674 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7676 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7677 MovDir[x][y] = right_dir;
7678 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7679 MovDir[x][y] = left_dir;
7681 if (MovDir[x][y] != old_move_dir)
7682 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7684 else if (move_pattern == MV_TOWARDS_PLAYER ||
7685 move_pattern == MV_AWAY_FROM_PLAYER)
7687 int attr_x = -1, attr_y = -1;
7689 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7700 for (i = 0; i < MAX_PLAYERS; i++)
7702 struct PlayerInfo *player = &stored_player[i];
7703 int jx = player->jx, jy = player->jy;
7705 if (!player->active)
7709 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7717 MovDir[x][y] = MV_NONE;
7719 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7720 else if (attr_x > x)
7721 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7723 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7724 else if (attr_y > y)
7725 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7727 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7729 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7731 boolean first_horiz = RND(2);
7732 int new_move_dir = MovDir[x][y];
7734 if (element_info[element].move_stepsize == 0) /* "not moving" */
7736 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7737 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7743 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744 Moving2Blocked(x, y, &newx, &newy);
7746 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7750 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7751 Moving2Blocked(x, y, &newx, &newy);
7753 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7756 MovDir[x][y] = old_move_dir;
7759 else if (move_pattern == MV_WHEN_PUSHED ||
7760 move_pattern == MV_WHEN_DROPPED)
7762 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7763 MovDir[x][y] = MV_NONE;
7767 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7769 static int test_xy[7][2] =
7779 static int test_dir[7] =
7789 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7790 int move_preference = -1000000; /* start with very low preference */
7791 int new_move_dir = MV_NONE;
7792 int start_test = RND(4);
7795 for (i = 0; i < NUM_DIRECTIONS; i++)
7797 int move_dir = test_dir[start_test + i];
7798 int move_dir_preference;
7800 xx = x + test_xy[start_test + i][0];
7801 yy = y + test_xy[start_test + i][1];
7803 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7804 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7806 new_move_dir = move_dir;
7811 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7814 move_dir_preference = -1 * RunnerVisit[xx][yy];
7815 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7816 move_dir_preference = PlayerVisit[xx][yy];
7818 if (move_dir_preference > move_preference)
7820 /* prefer field that has not been visited for the longest time */
7821 move_preference = move_dir_preference;
7822 new_move_dir = move_dir;
7824 else if (move_dir_preference == move_preference &&
7825 move_dir == old_move_dir)
7827 /* prefer last direction when all directions are preferred equally */
7828 move_preference = move_dir_preference;
7829 new_move_dir = move_dir;
7833 MovDir[x][y] = new_move_dir;
7834 if (old_move_dir != new_move_dir)
7835 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7839 static void TurnRound(int x, int y)
7841 int direction = MovDir[x][y];
7845 GfxDir[x][y] = MovDir[x][y];
7847 if (direction != MovDir[x][y])
7851 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7853 ResetGfxFrame(x, y, FALSE);
7856 static boolean JustBeingPushed(int x, int y)
7860 for (i = 0; i < MAX_PLAYERS; i++)
7862 struct PlayerInfo *player = &stored_player[i];
7864 if (player->active && player->is_pushing && player->MovPos)
7866 int next_jx = player->jx + (player->jx - player->last_jx);
7867 int next_jy = player->jy + (player->jy - player->last_jy);
7869 if (x == next_jx && y == next_jy)
7877 void StartMoving(int x, int y)
7879 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7880 int element = Feld[x][y];
7885 if (MovDelay[x][y] == 0)
7886 GfxAction[x][y] = ACTION_DEFAULT;
7888 if (CAN_FALL(element) && y < lev_fieldy - 1)
7890 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7891 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7892 if (JustBeingPushed(x, y))
7895 if (element == EL_QUICKSAND_FULL)
7897 if (IS_FREE(x, y + 1))
7899 InitMovingField(x, y, MV_DOWN);
7900 started_moving = TRUE;
7902 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7903 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7904 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7905 Store[x][y] = EL_ROCK;
7907 Store[x][y] = EL_ROCK;
7910 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7912 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7914 if (!MovDelay[x][y])
7916 MovDelay[x][y] = TILEY + 1;
7918 ResetGfxAnimation(x, y);
7919 ResetGfxAnimation(x, y + 1);
7924 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7925 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7932 Feld[x][y] = EL_QUICKSAND_EMPTY;
7933 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7934 Store[x][y + 1] = Store[x][y];
7937 PlayLevelSoundAction(x, y, ACTION_FILLING);
7939 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7941 if (!MovDelay[x][y])
7943 MovDelay[x][y] = TILEY + 1;
7945 ResetGfxAnimation(x, y);
7946 ResetGfxAnimation(x, y + 1);
7951 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7952 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7959 Feld[x][y] = EL_QUICKSAND_EMPTY;
7960 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7961 Store[x][y + 1] = Store[x][y];
7964 PlayLevelSoundAction(x, y, ACTION_FILLING);
7967 else if (element == EL_QUICKSAND_FAST_FULL)
7969 if (IS_FREE(x, y + 1))
7971 InitMovingField(x, y, MV_DOWN);
7972 started_moving = TRUE;
7974 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7976 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7977 Store[x][y] = EL_ROCK;
7979 Store[x][y] = EL_ROCK;
7982 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7984 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7986 if (!MovDelay[x][y])
7988 MovDelay[x][y] = TILEY + 1;
7990 ResetGfxAnimation(x, y);
7991 ResetGfxAnimation(x, y + 1);
7996 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7997 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8004 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8005 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8006 Store[x][y + 1] = Store[x][y];
8009 PlayLevelSoundAction(x, y, ACTION_FILLING);
8011 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8013 if (!MovDelay[x][y])
8015 MovDelay[x][y] = TILEY + 1;
8017 ResetGfxAnimation(x, y);
8018 ResetGfxAnimation(x, y + 1);
8023 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8024 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8031 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8032 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8033 Store[x][y + 1] = Store[x][y];
8036 PlayLevelSoundAction(x, y, ACTION_FILLING);
8039 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8040 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8042 InitMovingField(x, y, MV_DOWN);
8043 started_moving = TRUE;
8045 Feld[x][y] = EL_QUICKSAND_FILLING;
8046 Store[x][y] = element;
8048 PlayLevelSoundAction(x, y, ACTION_FILLING);
8050 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8051 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8053 InitMovingField(x, y, MV_DOWN);
8054 started_moving = TRUE;
8056 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8057 Store[x][y] = element;
8059 PlayLevelSoundAction(x, y, ACTION_FILLING);
8061 else if (element == EL_MAGIC_WALL_FULL)
8063 if (IS_FREE(x, y + 1))
8065 InitMovingField(x, y, MV_DOWN);
8066 started_moving = TRUE;
8068 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8069 Store[x][y] = EL_CHANGED(Store[x][y]);
8071 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8073 if (!MovDelay[x][y])
8074 MovDelay[x][y] = TILEY/4 + 1;
8083 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8084 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8085 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8089 else if (element == EL_BD_MAGIC_WALL_FULL)
8091 if (IS_FREE(x, y + 1))
8093 InitMovingField(x, y, MV_DOWN);
8094 started_moving = TRUE;
8096 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8097 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8099 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8101 if (!MovDelay[x][y])
8102 MovDelay[x][y] = TILEY/4 + 1;
8111 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8112 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8113 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8117 else if (element == EL_DC_MAGIC_WALL_FULL)
8119 if (IS_FREE(x, y + 1))
8121 InitMovingField(x, y, MV_DOWN);
8122 started_moving = TRUE;
8124 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8125 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8127 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8129 if (!MovDelay[x][y])
8130 MovDelay[x][y] = TILEY/4 + 1;
8139 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8140 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8141 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8145 else if ((CAN_PASS_MAGIC_WALL(element) &&
8146 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8147 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8148 (CAN_PASS_DC_MAGIC_WALL(element) &&
8149 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8152 InitMovingField(x, y, MV_DOWN);
8153 started_moving = TRUE;
8156 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8157 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8158 EL_DC_MAGIC_WALL_FILLING);
8159 Store[x][y] = element;
8161 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8163 SplashAcid(x, y + 1);
8165 InitMovingField(x, y, MV_DOWN);
8166 started_moving = TRUE;
8168 Store[x][y] = EL_ACID;
8171 #if USE_FIX_IMPACT_COLLISION
8172 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8173 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8175 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8176 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8178 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8179 CAN_FALL(element) && WasJustFalling[x][y] &&
8180 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8182 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8183 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8184 (Feld[x][y + 1] == EL_BLOCKED)))
8186 /* this is needed for a special case not covered by calling "Impact()"
8187 from "ContinueMoving()": if an element moves to a tile directly below
8188 another element which was just falling on that tile (which was empty
8189 in the previous frame), the falling element above would just stop
8190 instead of smashing the element below (in previous version, the above
8191 element was just checked for "moving" instead of "falling", resulting
8192 in incorrect smashes caused by horizontal movement of the above
8193 element; also, the case of the player being the element to smash was
8194 simply not covered here... :-/ ) */
8196 CheckCollision[x][y] = 0;
8197 CheckImpact[x][y] = 0;
8201 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8203 if (MovDir[x][y] == MV_NONE)
8205 InitMovingField(x, y, MV_DOWN);
8206 started_moving = TRUE;
8209 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8211 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8212 MovDir[x][y] = MV_DOWN;
8214 InitMovingField(x, y, MV_DOWN);
8215 started_moving = TRUE;
8217 else if (element == EL_AMOEBA_DROP)
8219 Feld[x][y] = EL_AMOEBA_GROWING;
8220 Store[x][y] = EL_AMOEBA_WET;
8222 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8223 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8224 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8225 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8227 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8228 (IS_FREE(x - 1, y + 1) ||
8229 Feld[x - 1][y + 1] == EL_ACID));
8230 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8231 (IS_FREE(x + 1, y + 1) ||
8232 Feld[x + 1][y + 1] == EL_ACID));
8233 boolean can_fall_any = (can_fall_left || can_fall_right);
8234 boolean can_fall_both = (can_fall_left && can_fall_right);
8235 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8237 #if USE_NEW_ALL_SLIPPERY
8238 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8240 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8241 can_fall_right = FALSE;
8242 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8243 can_fall_left = FALSE;
8244 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8245 can_fall_right = FALSE;
8246 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8247 can_fall_left = FALSE;
8249 can_fall_any = (can_fall_left || can_fall_right);
8250 can_fall_both = FALSE;
8253 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8255 if (slippery_type == SLIPPERY_ONLY_LEFT)
8256 can_fall_right = FALSE;
8257 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8258 can_fall_left = FALSE;
8259 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260 can_fall_right = FALSE;
8261 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262 can_fall_left = FALSE;
8264 can_fall_any = (can_fall_left || can_fall_right);
8265 can_fall_both = (can_fall_left && can_fall_right);
8269 #if USE_NEW_ALL_SLIPPERY
8271 #if USE_NEW_SP_SLIPPERY
8272 /* !!! better use the same properties as for custom elements here !!! */
8273 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8274 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8276 can_fall_right = FALSE; /* slip down on left side */
8277 can_fall_both = FALSE;
8282 #if USE_NEW_ALL_SLIPPERY
8285 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8286 can_fall_right = FALSE; /* slip down on left side */
8288 can_fall_left = !(can_fall_right = RND(2));
8290 can_fall_both = FALSE;
8295 if (game.emulation == EMU_BOULDERDASH ||
8296 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8297 can_fall_right = FALSE; /* slip down on left side */
8299 can_fall_left = !(can_fall_right = RND(2));
8301 can_fall_both = FALSE;
8307 /* if not determined otherwise, prefer left side for slipping down */
8308 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8309 started_moving = TRUE;
8313 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8315 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8318 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8319 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8320 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8321 int belt_dir = game.belt_dir[belt_nr];
8323 if ((belt_dir == MV_LEFT && left_is_free) ||
8324 (belt_dir == MV_RIGHT && right_is_free))
8326 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8328 InitMovingField(x, y, belt_dir);
8329 started_moving = TRUE;
8331 Pushed[x][y] = TRUE;
8332 Pushed[nextx][y] = TRUE;
8334 GfxAction[x][y] = ACTION_DEFAULT;
8338 MovDir[x][y] = 0; /* if element was moving, stop it */
8343 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8345 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8347 if (CAN_MOVE(element) && !started_moving)
8350 int move_pattern = element_info[element].move_pattern;
8355 if (MovDir[x][y] == MV_NONE)
8357 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8358 x, y, element, element_info[element].token_name);
8359 printf("StartMoving(): This should never happen!\n");
8364 Moving2Blocked(x, y, &newx, &newy);
8366 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8369 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8370 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8372 WasJustMoving[x][y] = 0;
8373 CheckCollision[x][y] = 0;
8375 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8377 if (Feld[x][y] != element) /* element has changed */
8381 if (!MovDelay[x][y]) /* start new movement phase */
8383 /* all objects that can change their move direction after each step
8384 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8386 if (element != EL_YAMYAM &&
8387 element != EL_DARK_YAMYAM &&
8388 element != EL_PACMAN &&
8389 !(move_pattern & MV_ANY_DIRECTION) &&
8390 move_pattern != MV_TURNING_LEFT &&
8391 move_pattern != MV_TURNING_RIGHT &&
8392 move_pattern != MV_TURNING_LEFT_RIGHT &&
8393 move_pattern != MV_TURNING_RIGHT_LEFT &&
8394 move_pattern != MV_TURNING_RANDOM)
8398 if (MovDelay[x][y] && (element == EL_BUG ||
8399 element == EL_SPACESHIP ||
8400 element == EL_SP_SNIKSNAK ||
8401 element == EL_SP_ELECTRON ||
8402 element == EL_MOLE))
8403 TEST_DrawLevelField(x, y);
8407 if (MovDelay[x][y]) /* wait some time before next movement */
8411 if (element == EL_ROBOT ||
8412 element == EL_YAMYAM ||
8413 element == EL_DARK_YAMYAM)
8415 DrawLevelElementAnimationIfNeeded(x, y, element);
8416 PlayLevelSoundAction(x, y, ACTION_WAITING);
8418 else if (element == EL_SP_ELECTRON)
8419 DrawLevelElementAnimationIfNeeded(x, y, element);
8420 else if (element == EL_DRAGON)
8423 int dir = MovDir[x][y];
8424 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8425 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8426 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8427 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8428 dir == MV_UP ? IMG_FLAMES_1_UP :
8429 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8430 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8432 GfxAction[x][y] = ACTION_ATTACKING;
8434 if (IS_PLAYER(x, y))
8435 DrawPlayerField(x, y);
8437 TEST_DrawLevelField(x, y);
8439 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8441 for (i = 1; i <= 3; i++)
8443 int xx = x + i * dx;
8444 int yy = y + i * dy;
8445 int sx = SCREENX(xx);
8446 int sy = SCREENY(yy);
8447 int flame_graphic = graphic + (i - 1);
8449 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8454 int flamed = MovingOrBlocked2Element(xx, yy);
8458 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8460 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8461 RemoveMovingField(xx, yy);
8463 RemoveField(xx, yy);
8465 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8468 RemoveMovingField(xx, yy);
8471 ChangeDelay[xx][yy] = 0;
8473 Feld[xx][yy] = EL_FLAMES;
8475 if (IN_SCR_FIELD(sx, sy))
8477 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8478 DrawGraphic(sx, sy, flame_graphic, frame);
8483 if (Feld[xx][yy] == EL_FLAMES)
8484 Feld[xx][yy] = EL_EMPTY;
8485 TEST_DrawLevelField(xx, yy);
8490 if (MovDelay[x][y]) /* element still has to wait some time */
8492 PlayLevelSoundAction(x, y, ACTION_WAITING);
8498 /* now make next step */
8500 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8502 if (DONT_COLLIDE_WITH(element) &&
8503 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8504 !PLAYER_ENEMY_PROTECTED(newx, newy))
8506 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8511 else if (CAN_MOVE_INTO_ACID(element) &&
8512 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8513 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8514 (MovDir[x][y] == MV_DOWN ||
8515 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8517 SplashAcid(newx, newy);
8518 Store[x][y] = EL_ACID;
8520 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8522 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8523 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8524 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8525 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8528 TEST_DrawLevelField(x, y);
8530 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8531 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8532 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8534 local_player->friends_still_needed--;
8535 if (!local_player->friends_still_needed &&
8536 !local_player->GameOver && AllPlayersGone)
8537 PlayerWins(local_player);
8541 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8543 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8544 TEST_DrawLevelField(newx, newy);
8546 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8548 else if (!IS_FREE(newx, newy))
8550 GfxAction[x][y] = ACTION_WAITING;
8552 if (IS_PLAYER(x, y))
8553 DrawPlayerField(x, y);
8555 TEST_DrawLevelField(x, y);
8560 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8562 if (IS_FOOD_PIG(Feld[newx][newy]))
8564 if (IS_MOVING(newx, newy))
8565 RemoveMovingField(newx, newy);
8568 Feld[newx][newy] = EL_EMPTY;
8569 TEST_DrawLevelField(newx, newy);
8572 PlayLevelSound(x, y, SND_PIG_DIGGING);
8574 else if (!IS_FREE(newx, newy))
8576 if (IS_PLAYER(x, y))
8577 DrawPlayerField(x, y);
8579 TEST_DrawLevelField(x, y);
8584 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8586 if (Store[x][y] != EL_EMPTY)
8588 boolean can_clone = FALSE;
8591 /* check if element to clone is still there */
8592 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8594 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8602 /* cannot clone or target field not free anymore -- do not clone */
8603 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8604 Store[x][y] = EL_EMPTY;
8607 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8609 if (IS_MV_DIAGONAL(MovDir[x][y]))
8611 int diagonal_move_dir = MovDir[x][y];
8612 int stored = Store[x][y];
8613 int change_delay = 8;
8616 /* android is moving diagonally */
8618 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8620 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8621 GfxElement[x][y] = EL_EMC_ANDROID;
8622 GfxAction[x][y] = ACTION_SHRINKING;
8623 GfxDir[x][y] = diagonal_move_dir;
8624 ChangeDelay[x][y] = change_delay;
8626 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8629 DrawLevelGraphicAnimation(x, y, graphic);
8630 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8632 if (Feld[newx][newy] == EL_ACID)
8634 SplashAcid(newx, newy);
8639 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8641 Store[newx][newy] = EL_EMC_ANDROID;
8642 GfxElement[newx][newy] = EL_EMC_ANDROID;
8643 GfxAction[newx][newy] = ACTION_GROWING;
8644 GfxDir[newx][newy] = diagonal_move_dir;
8645 ChangeDelay[newx][newy] = change_delay;
8647 graphic = el_act_dir2img(GfxElement[newx][newy],
8648 GfxAction[newx][newy], GfxDir[newx][newy]);
8650 DrawLevelGraphicAnimation(newx, newy, graphic);
8651 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8657 Feld[newx][newy] = EL_EMPTY;
8658 TEST_DrawLevelField(newx, newy);
8660 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8663 else if (!IS_FREE(newx, newy))
8666 if (IS_PLAYER(x, y))
8667 DrawPlayerField(x, y);
8669 TEST_DrawLevelField(x, y);
8675 else if (IS_CUSTOM_ELEMENT(element) &&
8676 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8679 if (!DigFieldByCE(newx, newy, element))
8682 int new_element = Feld[newx][newy];
8684 if (!IS_FREE(newx, newy))
8686 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8687 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8690 /* no element can dig solid indestructible elements */
8691 if (IS_INDESTRUCTIBLE(new_element) &&
8692 !IS_DIGGABLE(new_element) &&
8693 !IS_COLLECTIBLE(new_element))
8696 if (AmoebaNr[newx][newy] &&
8697 (new_element == EL_AMOEBA_FULL ||
8698 new_element == EL_BD_AMOEBA ||
8699 new_element == EL_AMOEBA_GROWING))
8701 AmoebaCnt[AmoebaNr[newx][newy]]--;
8702 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8705 if (IS_MOVING(newx, newy))
8706 RemoveMovingField(newx, newy);
8709 RemoveField(newx, newy);
8710 TEST_DrawLevelField(newx, newy);
8713 /* if digged element was about to explode, prevent the explosion */
8714 ExplodeField[newx][newy] = EX_TYPE_NONE;
8716 PlayLevelSoundAction(x, y, action);
8719 Store[newx][newy] = EL_EMPTY;
8722 /* this makes it possible to leave the removed element again */
8723 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8724 Store[newx][newy] = new_element;
8726 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8728 int move_leave_element = element_info[element].move_leave_element;
8730 /* this makes it possible to leave the removed element again */
8731 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8732 new_element : move_leave_element);
8738 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8740 RunnerVisit[x][y] = FrameCounter;
8741 PlayerVisit[x][y] /= 8; /* expire player visit path */
8744 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8746 if (!IS_FREE(newx, newy))
8748 if (IS_PLAYER(x, y))
8749 DrawPlayerField(x, y);
8751 TEST_DrawLevelField(x, y);
8757 boolean wanna_flame = !RND(10);
8758 int dx = newx - x, dy = newy - y;
8759 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8760 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8761 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8762 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8763 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8764 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8767 IS_CLASSIC_ENEMY(element1) ||
8768 IS_CLASSIC_ENEMY(element2)) &&
8769 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8770 element1 != EL_FLAMES && element2 != EL_FLAMES)
8772 ResetGfxAnimation(x, y);
8773 GfxAction[x][y] = ACTION_ATTACKING;
8775 if (IS_PLAYER(x, y))
8776 DrawPlayerField(x, y);
8778 TEST_DrawLevelField(x, y);
8780 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8782 MovDelay[x][y] = 50;
8786 RemoveField(newx, newy);
8788 Feld[newx][newy] = EL_FLAMES;
8789 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8792 RemoveField(newx1, newy1);
8794 Feld[newx1][newy1] = EL_FLAMES;
8796 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8799 RemoveField(newx2, newy2);
8801 Feld[newx2][newy2] = EL_FLAMES;
8808 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8809 Feld[newx][newy] == EL_DIAMOND)
8811 if (IS_MOVING(newx, newy))
8812 RemoveMovingField(newx, newy);
8815 Feld[newx][newy] = EL_EMPTY;
8816 TEST_DrawLevelField(newx, newy);
8819 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8821 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8822 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8824 if (AmoebaNr[newx][newy])
8826 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8827 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8828 Feld[newx][newy] == EL_BD_AMOEBA)
8829 AmoebaCnt[AmoebaNr[newx][newy]]--;
8834 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8836 RemoveMovingField(newx, newy);
8839 if (IS_MOVING(newx, newy))
8841 RemoveMovingField(newx, newy);
8846 Feld[newx][newy] = EL_EMPTY;
8847 TEST_DrawLevelField(newx, newy);
8850 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8852 else if ((element == EL_PACMAN || element == EL_MOLE)
8853 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8855 if (AmoebaNr[newx][newy])
8857 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8858 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8859 Feld[newx][newy] == EL_BD_AMOEBA)
8860 AmoebaCnt[AmoebaNr[newx][newy]]--;
8863 if (element == EL_MOLE)
8865 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8866 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8868 ResetGfxAnimation(x, y);
8869 GfxAction[x][y] = ACTION_DIGGING;
8870 TEST_DrawLevelField(x, y);
8872 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8874 return; /* wait for shrinking amoeba */
8876 else /* element == EL_PACMAN */
8878 Feld[newx][newy] = EL_EMPTY;
8879 TEST_DrawLevelField(newx, newy);
8880 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8883 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8884 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8885 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8887 /* wait for shrinking amoeba to completely disappear */
8890 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8892 /* object was running against a wall */
8897 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8898 if (move_pattern & MV_ANY_DIRECTION &&
8899 move_pattern == MovDir[x][y])
8901 int blocking_element =
8902 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8904 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8907 element = Feld[x][y]; /* element might have changed */
8911 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8912 DrawLevelElementAnimation(x, y, element);
8914 if (DONT_TOUCH(element))
8915 TestIfBadThingTouchesPlayer(x, y);
8920 InitMovingField(x, y, MovDir[x][y]);
8922 PlayLevelSoundAction(x, y, ACTION_MOVING);
8926 ContinueMoving(x, y);
8929 void ContinueMoving(int x, int y)
8931 int element = Feld[x][y];
8932 struct ElementInfo *ei = &element_info[element];
8933 int direction = MovDir[x][y];
8934 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8935 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8936 int newx = x + dx, newy = y + dy;
8937 int stored = Store[x][y];
8938 int stored_new = Store[newx][newy];
8939 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8940 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8941 boolean last_line = (newy == lev_fieldy - 1);
8943 MovPos[x][y] += getElementMoveStepsize(x, y);
8945 if (pushed_by_player) /* special case: moving object pushed by player */
8946 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8948 if (ABS(MovPos[x][y]) < TILEX)
8951 int ee = Feld[x][y];
8952 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8953 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8955 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8956 x, y, ABS(MovPos[x][y]),
8958 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8961 TEST_DrawLevelField(x, y);
8963 return; /* element is still moving */
8966 /* element reached destination field */
8968 Feld[x][y] = EL_EMPTY;
8969 Feld[newx][newy] = element;
8970 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8972 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8974 element = Feld[newx][newy] = EL_ACID;
8976 else if (element == EL_MOLE)
8978 Feld[x][y] = EL_SAND;
8980 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8982 else if (element == EL_QUICKSAND_FILLING)
8984 element = Feld[newx][newy] = get_next_element(element);
8985 Store[newx][newy] = Store[x][y];
8987 else if (element == EL_QUICKSAND_EMPTYING)
8989 Feld[x][y] = get_next_element(element);
8990 element = Feld[newx][newy] = Store[x][y];
8992 else if (element == EL_QUICKSAND_FAST_FILLING)
8994 element = Feld[newx][newy] = get_next_element(element);
8995 Store[newx][newy] = Store[x][y];
8997 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8999 Feld[x][y] = get_next_element(element);
9000 element = Feld[newx][newy] = Store[x][y];
9002 else if (element == EL_MAGIC_WALL_FILLING)
9004 element = Feld[newx][newy] = get_next_element(element);
9005 if (!game.magic_wall_active)
9006 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9007 Store[newx][newy] = Store[x][y];
9009 else if (element == EL_MAGIC_WALL_EMPTYING)
9011 Feld[x][y] = get_next_element(element);
9012 if (!game.magic_wall_active)
9013 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9014 element = Feld[newx][newy] = Store[x][y];
9016 #if USE_NEW_CUSTOM_VALUE
9017 InitField(newx, newy, FALSE);
9020 else if (element == EL_BD_MAGIC_WALL_FILLING)
9022 element = Feld[newx][newy] = get_next_element(element);
9023 if (!game.magic_wall_active)
9024 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9025 Store[newx][newy] = Store[x][y];
9027 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9029 Feld[x][y] = get_next_element(element);
9030 if (!game.magic_wall_active)
9031 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9032 element = Feld[newx][newy] = Store[x][y];
9034 #if USE_NEW_CUSTOM_VALUE
9035 InitField(newx, newy, FALSE);
9038 else if (element == EL_DC_MAGIC_WALL_FILLING)
9040 element = Feld[newx][newy] = get_next_element(element);
9041 if (!game.magic_wall_active)
9042 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9043 Store[newx][newy] = Store[x][y];
9045 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9047 Feld[x][y] = get_next_element(element);
9048 if (!game.magic_wall_active)
9049 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9050 element = Feld[newx][newy] = Store[x][y];
9052 #if USE_NEW_CUSTOM_VALUE
9053 InitField(newx, newy, FALSE);
9056 else if (element == EL_AMOEBA_DROPPING)
9058 Feld[x][y] = get_next_element(element);
9059 element = Feld[newx][newy] = Store[x][y];
9061 else if (element == EL_SOKOBAN_OBJECT)
9064 Feld[x][y] = Back[x][y];
9066 if (Back[newx][newy])
9067 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9069 Back[x][y] = Back[newx][newy] = 0;
9072 Store[x][y] = EL_EMPTY;
9077 MovDelay[newx][newy] = 0;
9079 if (CAN_CHANGE_OR_HAS_ACTION(element))
9081 /* copy element change control values to new field */
9082 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9083 ChangePage[newx][newy] = ChangePage[x][y];
9084 ChangeCount[newx][newy] = ChangeCount[x][y];
9085 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9088 #if USE_NEW_CUSTOM_VALUE
9089 CustomValue[newx][newy] = CustomValue[x][y];
9092 ChangeDelay[x][y] = 0;
9093 ChangePage[x][y] = -1;
9094 ChangeCount[x][y] = 0;
9095 ChangeEvent[x][y] = -1;
9097 #if USE_NEW_CUSTOM_VALUE
9098 CustomValue[x][y] = 0;
9101 /* copy animation control values to new field */
9102 GfxFrame[newx][newy] = GfxFrame[x][y];
9103 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9104 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9105 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9107 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9109 /* some elements can leave other elements behind after moving */
9111 if (ei->move_leave_element != EL_EMPTY &&
9112 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9113 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9115 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9116 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9117 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9120 int move_leave_element = ei->move_leave_element;
9124 /* this makes it possible to leave the removed element again */
9125 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9126 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9128 /* this makes it possible to leave the removed element again */
9129 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9130 move_leave_element = stored;
9133 /* this makes it possible to leave the removed element again */
9134 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9135 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9136 move_leave_element = stored;
9139 Feld[x][y] = move_leave_element;
9141 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9142 MovDir[x][y] = direction;
9144 InitField(x, y, FALSE);
9146 if (GFX_CRUMBLED(Feld[x][y]))
9147 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9149 if (ELEM_IS_PLAYER(move_leave_element))
9150 RelocatePlayer(x, y, move_leave_element);
9153 /* do this after checking for left-behind element */
9154 ResetGfxAnimation(x, y); /* reset animation values for old field */
9156 if (!CAN_MOVE(element) ||
9157 (CAN_FALL(element) && direction == MV_DOWN &&
9158 (element == EL_SPRING ||
9159 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9160 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9161 GfxDir[x][y] = MovDir[newx][newy] = 0;
9163 TEST_DrawLevelField(x, y);
9164 TEST_DrawLevelField(newx, newy);
9166 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9168 /* prevent pushed element from moving on in pushed direction */
9169 if (pushed_by_player && CAN_MOVE(element) &&
9170 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9171 !(element_info[element].move_pattern & direction))
9172 TurnRound(newx, newy);
9174 /* prevent elements on conveyor belt from moving on in last direction */
9175 if (pushed_by_conveyor && CAN_FALL(element) &&
9176 direction & MV_HORIZONTAL)
9177 MovDir[newx][newy] = 0;
9179 if (!pushed_by_player)
9181 int nextx = newx + dx, nexty = newy + dy;
9182 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9184 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9186 if (CAN_FALL(element) && direction == MV_DOWN)
9187 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9189 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9190 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9192 #if USE_FIX_IMPACT_COLLISION
9193 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9194 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9198 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9200 TestIfBadThingTouchesPlayer(newx, newy);
9201 TestIfBadThingTouchesFriend(newx, newy);
9203 if (!IS_CUSTOM_ELEMENT(element))
9204 TestIfBadThingTouchesOtherBadThing(newx, newy);
9206 else if (element == EL_PENGUIN)
9207 TestIfFriendTouchesBadThing(newx, newy);
9209 if (DONT_GET_HIT_BY(element))
9211 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9214 /* give the player one last chance (one more frame) to move away */
9215 if (CAN_FALL(element) && direction == MV_DOWN &&
9216 (last_line || (!IS_FREE(x, newy + 1) &&
9217 (!IS_PLAYER(x, newy + 1) ||
9218 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9221 if (pushed_by_player && !game.use_change_when_pushing_bug)
9223 int push_side = MV_DIR_OPPOSITE(direction);
9224 struct PlayerInfo *player = PLAYERINFO(x, y);
9226 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9227 player->index_bit, push_side);
9228 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9229 player->index_bit, push_side);
9232 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9233 MovDelay[newx][newy] = 1;
9235 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9237 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9240 if (ChangePage[newx][newy] != -1) /* delayed change */
9242 int page = ChangePage[newx][newy];
9243 struct ElementChangeInfo *change = &ei->change_page[page];
9245 ChangePage[newx][newy] = -1;
9247 if (change->can_change)
9249 if (ChangeElement(newx, newy, element, page))
9251 if (change->post_change_function)
9252 change->post_change_function(newx, newy);
9256 if (change->has_action)
9257 ExecuteCustomElementAction(newx, newy, element, page);
9261 TestIfElementHitsCustomElement(newx, newy, direction);
9262 TestIfPlayerTouchesCustomElement(newx, newy);
9263 TestIfElementTouchesCustomElement(newx, newy);
9265 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9266 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9267 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9268 MV_DIR_OPPOSITE(direction));
9271 int AmoebeNachbarNr(int ax, int ay)
9274 int element = Feld[ax][ay];
9276 static int xy[4][2] =
9284 for (i = 0; i < NUM_DIRECTIONS; i++)
9286 int x = ax + xy[i][0];
9287 int y = ay + xy[i][1];
9289 if (!IN_LEV_FIELD(x, y))
9292 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9293 group_nr = AmoebaNr[x][y];
9299 void AmoebenVereinigen(int ax, int ay)
9301 int i, x, y, xx, yy;
9302 int new_group_nr = AmoebaNr[ax][ay];
9303 static int xy[4][2] =
9311 if (new_group_nr == 0)
9314 for (i = 0; i < NUM_DIRECTIONS; i++)
9319 if (!IN_LEV_FIELD(x, y))
9322 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9323 Feld[x][y] == EL_BD_AMOEBA ||
9324 Feld[x][y] == EL_AMOEBA_DEAD) &&
9325 AmoebaNr[x][y] != new_group_nr)
9327 int old_group_nr = AmoebaNr[x][y];
9329 if (old_group_nr == 0)
9332 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9333 AmoebaCnt[old_group_nr] = 0;
9334 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9335 AmoebaCnt2[old_group_nr] = 0;
9337 SCAN_PLAYFIELD(xx, yy)
9339 if (AmoebaNr[xx][yy] == old_group_nr)
9340 AmoebaNr[xx][yy] = new_group_nr;
9346 void AmoebeUmwandeln(int ax, int ay)
9350 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9352 int group_nr = AmoebaNr[ax][ay];
9357 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9358 printf("AmoebeUmwandeln(): This should never happen!\n");
9363 SCAN_PLAYFIELD(x, y)
9365 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9368 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9372 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9373 SND_AMOEBA_TURNING_TO_GEM :
9374 SND_AMOEBA_TURNING_TO_ROCK));
9379 static int xy[4][2] =
9387 for (i = 0; i < NUM_DIRECTIONS; i++)
9392 if (!IN_LEV_FIELD(x, y))
9395 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9397 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9398 SND_AMOEBA_TURNING_TO_GEM :
9399 SND_AMOEBA_TURNING_TO_ROCK));
9406 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9409 int group_nr = AmoebaNr[ax][ay];
9410 boolean done = FALSE;
9415 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9416 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9421 SCAN_PLAYFIELD(x, y)
9423 if (AmoebaNr[x][y] == group_nr &&
9424 (Feld[x][y] == EL_AMOEBA_DEAD ||
9425 Feld[x][y] == EL_BD_AMOEBA ||
9426 Feld[x][y] == EL_AMOEBA_GROWING))
9429 Feld[x][y] = new_element;
9430 InitField(x, y, FALSE);
9431 TEST_DrawLevelField(x, y);
9437 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9438 SND_BD_AMOEBA_TURNING_TO_ROCK :
9439 SND_BD_AMOEBA_TURNING_TO_GEM));
9442 void AmoebeWaechst(int x, int y)
9444 static unsigned long sound_delay = 0;
9445 static unsigned long sound_delay_value = 0;
9447 if (!MovDelay[x][y]) /* start new growing cycle */
9451 if (DelayReached(&sound_delay, sound_delay_value))
9453 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9454 sound_delay_value = 30;
9458 if (MovDelay[x][y]) /* wait some time before growing bigger */
9461 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9463 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9464 6 - MovDelay[x][y]);
9466 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9469 if (!MovDelay[x][y])
9471 Feld[x][y] = Store[x][y];
9473 TEST_DrawLevelField(x, y);
9478 void AmoebaDisappearing(int x, int y)
9480 static unsigned long sound_delay = 0;
9481 static unsigned long sound_delay_value = 0;
9483 if (!MovDelay[x][y]) /* start new shrinking cycle */
9487 if (DelayReached(&sound_delay, sound_delay_value))
9488 sound_delay_value = 30;
9491 if (MovDelay[x][y]) /* wait some time before shrinking */
9494 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9496 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9497 6 - MovDelay[x][y]);
9499 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9502 if (!MovDelay[x][y])
9504 Feld[x][y] = EL_EMPTY;
9505 TEST_DrawLevelField(x, y);
9507 /* don't let mole enter this field in this cycle;
9508 (give priority to objects falling to this field from above) */
9514 void AmoebeAbleger(int ax, int ay)
9517 int element = Feld[ax][ay];
9518 int graphic = el2img(element);
9519 int newax = ax, neway = ay;
9520 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9521 static int xy[4][2] =
9529 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9531 Feld[ax][ay] = EL_AMOEBA_DEAD;
9532 TEST_DrawLevelField(ax, ay);
9536 if (IS_ANIMATED(graphic))
9537 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9539 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9540 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9542 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9545 if (MovDelay[ax][ay])
9549 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9552 int x = ax + xy[start][0];
9553 int y = ay + xy[start][1];
9555 if (!IN_LEV_FIELD(x, y))
9558 if (IS_FREE(x, y) ||
9559 CAN_GROW_INTO(Feld[x][y]) ||
9560 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9561 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9567 if (newax == ax && neway == ay)
9570 else /* normal or "filled" (BD style) amoeba */
9573 boolean waiting_for_player = FALSE;
9575 for (i = 0; i < NUM_DIRECTIONS; i++)
9577 int j = (start + i) % 4;
9578 int x = ax + xy[j][0];
9579 int y = ay + xy[j][1];
9581 if (!IN_LEV_FIELD(x, y))
9584 if (IS_FREE(x, y) ||
9585 CAN_GROW_INTO(Feld[x][y]) ||
9586 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9587 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9593 else if (IS_PLAYER(x, y))
9594 waiting_for_player = TRUE;
9597 if (newax == ax && neway == ay) /* amoeba cannot grow */
9599 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9601 Feld[ax][ay] = EL_AMOEBA_DEAD;
9602 TEST_DrawLevelField(ax, ay);
9603 AmoebaCnt[AmoebaNr[ax][ay]]--;
9605 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9607 if (element == EL_AMOEBA_FULL)
9608 AmoebeUmwandeln(ax, ay);
9609 else if (element == EL_BD_AMOEBA)
9610 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9615 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9617 /* amoeba gets larger by growing in some direction */
9619 int new_group_nr = AmoebaNr[ax][ay];
9622 if (new_group_nr == 0)
9624 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9625 printf("AmoebeAbleger(): This should never happen!\n");
9630 AmoebaNr[newax][neway] = new_group_nr;
9631 AmoebaCnt[new_group_nr]++;
9632 AmoebaCnt2[new_group_nr]++;
9634 /* if amoeba touches other amoeba(s) after growing, unify them */
9635 AmoebenVereinigen(newax, neway);
9637 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9639 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9645 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9646 (neway == lev_fieldy - 1 && newax != ax))
9648 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9649 Store[newax][neway] = element;
9651 else if (neway == ay || element == EL_EMC_DRIPPER)
9653 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9655 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9659 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9660 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9661 Store[ax][ay] = EL_AMOEBA_DROP;
9662 ContinueMoving(ax, ay);
9666 TEST_DrawLevelField(newax, neway);
9669 void Life(int ax, int ay)
9673 int element = Feld[ax][ay];
9674 int graphic = el2img(element);
9675 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9677 boolean changed = FALSE;
9679 if (IS_ANIMATED(graphic))
9680 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9685 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9686 MovDelay[ax][ay] = life_time;
9688 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9691 if (MovDelay[ax][ay])
9695 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9697 int xx = ax+x1, yy = ay+y1;
9700 if (!IN_LEV_FIELD(xx, yy))
9703 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9705 int x = xx+x2, y = yy+y2;
9707 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9710 if (((Feld[x][y] == element ||
9711 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9713 (IS_FREE(x, y) && Stop[x][y]))
9717 if (xx == ax && yy == ay) /* field in the middle */
9719 if (nachbarn < life_parameter[0] ||
9720 nachbarn > life_parameter[1])
9722 Feld[xx][yy] = EL_EMPTY;
9724 TEST_DrawLevelField(xx, yy);
9725 Stop[xx][yy] = TRUE;
9729 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9730 { /* free border field */
9731 if (nachbarn >= life_parameter[2] &&
9732 nachbarn <= life_parameter[3])
9734 Feld[xx][yy] = element;
9735 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9737 TEST_DrawLevelField(xx, yy);
9738 Stop[xx][yy] = TRUE;
9745 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9746 SND_GAME_OF_LIFE_GROWING);
9749 static void InitRobotWheel(int x, int y)
9751 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9754 static void RunRobotWheel(int x, int y)
9756 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9759 static void StopRobotWheel(int x, int y)
9761 if (ZX == x && ZY == y)
9765 game.robot_wheel_active = FALSE;
9769 static void InitTimegateWheel(int x, int y)
9771 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9774 static void RunTimegateWheel(int x, int y)
9776 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9779 static void InitMagicBallDelay(int x, int y)
9782 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9784 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9788 static void ActivateMagicBall(int bx, int by)
9792 if (level.ball_random)
9794 int pos_border = RND(8); /* select one of the eight border elements */
9795 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9796 int xx = pos_content % 3;
9797 int yy = pos_content / 3;
9802 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9803 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9807 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9809 int xx = x - bx + 1;
9810 int yy = y - by + 1;
9812 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9813 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9817 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9820 void CheckExit(int x, int y)
9822 if (local_player->gems_still_needed > 0 ||
9823 local_player->sokobanfields_still_needed > 0 ||
9824 local_player->lights_still_needed > 0)
9826 int element = Feld[x][y];
9827 int graphic = el2img(element);
9829 if (IS_ANIMATED(graphic))
9830 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9835 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9838 Feld[x][y] = EL_EXIT_OPENING;
9840 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9843 void CheckExitEM(int x, int y)
9845 if (local_player->gems_still_needed > 0 ||
9846 local_player->sokobanfields_still_needed > 0 ||
9847 local_player->lights_still_needed > 0)
9849 int element = Feld[x][y];
9850 int graphic = el2img(element);
9852 if (IS_ANIMATED(graphic))
9853 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9858 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9861 Feld[x][y] = EL_EM_EXIT_OPENING;
9863 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9866 void CheckExitSteel(int x, int y)
9868 if (local_player->gems_still_needed > 0 ||
9869 local_player->sokobanfields_still_needed > 0 ||
9870 local_player->lights_still_needed > 0)
9872 int element = Feld[x][y];
9873 int graphic = el2img(element);
9875 if (IS_ANIMATED(graphic))
9876 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9881 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9884 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9886 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9889 void CheckExitSteelEM(int x, int y)
9891 if (local_player->gems_still_needed > 0 ||
9892 local_player->sokobanfields_still_needed > 0 ||
9893 local_player->lights_still_needed > 0)
9895 int element = Feld[x][y];
9896 int graphic = el2img(element);
9898 if (IS_ANIMATED(graphic))
9899 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9904 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9907 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9909 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9912 void CheckExitSP(int x, int y)
9914 if (local_player->gems_still_needed > 0)
9916 int element = Feld[x][y];
9917 int graphic = el2img(element);
9919 if (IS_ANIMATED(graphic))
9920 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9925 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9928 Feld[x][y] = EL_SP_EXIT_OPENING;
9930 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9933 static void CloseAllOpenTimegates()
9937 SCAN_PLAYFIELD(x, y)
9939 int element = Feld[x][y];
9941 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9943 Feld[x][y] = EL_TIMEGATE_CLOSING;
9945 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9950 void DrawTwinkleOnField(int x, int y)
9952 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9955 if (Feld[x][y] == EL_BD_DIAMOND)
9958 if (MovDelay[x][y] == 0) /* next animation frame */
9959 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9961 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9965 DrawLevelElementAnimation(x, y, Feld[x][y]);
9967 if (MovDelay[x][y] != 0)
9969 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9970 10 - MovDelay[x][y]);
9972 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9977 void MauerWaechst(int x, int y)
9981 if (!MovDelay[x][y]) /* next animation frame */
9982 MovDelay[x][y] = 3 * delay;
9984 if (MovDelay[x][y]) /* wait some time before next frame */
9988 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9990 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9991 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9993 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9996 if (!MovDelay[x][y])
9998 if (MovDir[x][y] == MV_LEFT)
10000 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10001 TEST_DrawLevelField(x - 1, y);
10003 else if (MovDir[x][y] == MV_RIGHT)
10005 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10006 TEST_DrawLevelField(x + 1, y);
10008 else if (MovDir[x][y] == MV_UP)
10010 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10011 TEST_DrawLevelField(x, y - 1);
10015 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10016 TEST_DrawLevelField(x, y + 1);
10019 Feld[x][y] = Store[x][y];
10021 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10022 TEST_DrawLevelField(x, y);
10027 void MauerAbleger(int ax, int ay)
10029 int element = Feld[ax][ay];
10030 int graphic = el2img(element);
10031 boolean oben_frei = FALSE, unten_frei = FALSE;
10032 boolean links_frei = FALSE, rechts_frei = FALSE;
10033 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10034 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10035 boolean new_wall = FALSE;
10037 if (IS_ANIMATED(graphic))
10038 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10040 if (!MovDelay[ax][ay]) /* start building new wall */
10041 MovDelay[ax][ay] = 6;
10043 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10045 MovDelay[ax][ay]--;
10046 if (MovDelay[ax][ay])
10050 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10052 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10054 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10056 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10057 rechts_frei = TRUE;
10059 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10060 element == EL_EXPANDABLE_WALL_ANY)
10064 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10065 Store[ax][ay-1] = element;
10066 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10067 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10068 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10069 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10074 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10075 Store[ax][ay+1] = element;
10076 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10077 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10078 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10079 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10084 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10085 element == EL_EXPANDABLE_WALL_ANY ||
10086 element == EL_EXPANDABLE_WALL ||
10087 element == EL_BD_EXPANDABLE_WALL)
10091 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10092 Store[ax-1][ay] = element;
10093 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10094 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10095 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10096 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10102 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10103 Store[ax+1][ay] = element;
10104 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10105 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10106 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10107 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10112 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10113 TEST_DrawLevelField(ax, ay);
10115 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10116 oben_massiv = TRUE;
10117 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10118 unten_massiv = TRUE;
10119 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10120 links_massiv = TRUE;
10121 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10122 rechts_massiv = TRUE;
10124 if (((oben_massiv && unten_massiv) ||
10125 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10126 element == EL_EXPANDABLE_WALL) &&
10127 ((links_massiv && rechts_massiv) ||
10128 element == EL_EXPANDABLE_WALL_VERTICAL))
10129 Feld[ax][ay] = EL_WALL;
10132 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10135 void MauerAblegerStahl(int ax, int ay)
10137 int element = Feld[ax][ay];
10138 int graphic = el2img(element);
10139 boolean oben_frei = FALSE, unten_frei = FALSE;
10140 boolean links_frei = FALSE, rechts_frei = FALSE;
10141 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10142 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10143 boolean new_wall = FALSE;
10145 if (IS_ANIMATED(graphic))
10146 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10148 if (!MovDelay[ax][ay]) /* start building new wall */
10149 MovDelay[ax][ay] = 6;
10151 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10153 MovDelay[ax][ay]--;
10154 if (MovDelay[ax][ay])
10158 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10160 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10162 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10164 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10165 rechts_frei = TRUE;
10167 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10168 element == EL_EXPANDABLE_STEELWALL_ANY)
10172 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10173 Store[ax][ay-1] = element;
10174 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10175 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10176 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10177 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10182 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10183 Store[ax][ay+1] = element;
10184 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10185 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10186 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10187 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10192 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10193 element == EL_EXPANDABLE_STEELWALL_ANY)
10197 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10198 Store[ax-1][ay] = element;
10199 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10200 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10201 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10202 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10208 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10209 Store[ax+1][ay] = element;
10210 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10211 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10212 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10213 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10218 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10219 oben_massiv = TRUE;
10220 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10221 unten_massiv = TRUE;
10222 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10223 links_massiv = TRUE;
10224 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10225 rechts_massiv = TRUE;
10227 if (((oben_massiv && unten_massiv) ||
10228 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10229 ((links_massiv && rechts_massiv) ||
10230 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10231 Feld[ax][ay] = EL_STEELWALL;
10234 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10237 void CheckForDragon(int x, int y)
10240 boolean dragon_found = FALSE;
10241 static int xy[4][2] =
10249 for (i = 0; i < NUM_DIRECTIONS; i++)
10251 for (j = 0; j < 4; j++)
10253 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10255 if (IN_LEV_FIELD(xx, yy) &&
10256 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10258 if (Feld[xx][yy] == EL_DRAGON)
10259 dragon_found = TRUE;
10268 for (i = 0; i < NUM_DIRECTIONS; i++)
10270 for (j = 0; j < 3; j++)
10272 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10274 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10276 Feld[xx][yy] = EL_EMPTY;
10277 TEST_DrawLevelField(xx, yy);
10286 static void InitBuggyBase(int x, int y)
10288 int element = Feld[x][y];
10289 int activating_delay = FRAMES_PER_SECOND / 4;
10291 ChangeDelay[x][y] =
10292 (element == EL_SP_BUGGY_BASE ?
10293 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10294 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10296 element == EL_SP_BUGGY_BASE_ACTIVE ?
10297 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10300 static void WarnBuggyBase(int x, int y)
10303 static int xy[4][2] =
10311 for (i = 0; i < NUM_DIRECTIONS; i++)
10313 int xx = x + xy[i][0];
10314 int yy = y + xy[i][1];
10316 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10318 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10325 static void InitTrap(int x, int y)
10327 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10330 static void ActivateTrap(int x, int y)
10332 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10335 static void ChangeActiveTrap(int x, int y)
10337 int graphic = IMG_TRAP_ACTIVE;
10339 /* if new animation frame was drawn, correct crumbled sand border */
10340 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10341 TEST_DrawLevelFieldCrumbledSand(x, y);
10344 static int getSpecialActionElement(int element, int number, int base_element)
10346 return (element != EL_EMPTY ? element :
10347 number != -1 ? base_element + number - 1 :
10351 static int getModifiedActionNumber(int value_old, int operator, int operand,
10352 int value_min, int value_max)
10354 int value_new = (operator == CA_MODE_SET ? operand :
10355 operator == CA_MODE_ADD ? value_old + operand :
10356 operator == CA_MODE_SUBTRACT ? value_old - operand :
10357 operator == CA_MODE_MULTIPLY ? value_old * operand :
10358 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10359 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10362 return (value_new < value_min ? value_min :
10363 value_new > value_max ? value_max :
10367 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10369 struct ElementInfo *ei = &element_info[element];
10370 struct ElementChangeInfo *change = &ei->change_page[page];
10371 int target_element = change->target_element;
10372 int action_type = change->action_type;
10373 int action_mode = change->action_mode;
10374 int action_arg = change->action_arg;
10375 int action_element = change->action_element;
10378 if (!change->has_action)
10381 /* ---------- determine action paramater values -------------------------- */
10383 int level_time_value =
10384 (level.time > 0 ? TimeLeft :
10387 int action_arg_element_raw =
10388 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10389 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10390 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10391 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10392 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10393 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10394 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10396 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10399 if (action_arg_element_raw == EL_GROUP_START)
10400 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10403 int action_arg_direction =
10404 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10405 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10406 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10407 change->actual_trigger_side :
10408 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10409 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10412 int action_arg_number_min =
10413 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10416 int action_arg_number_max =
10417 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10418 action_type == CA_SET_LEVEL_GEMS ? 999 :
10419 action_type == CA_SET_LEVEL_TIME ? 9999 :
10420 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10421 action_type == CA_SET_CE_VALUE ? 9999 :
10422 action_type == CA_SET_CE_SCORE ? 9999 :
10425 int action_arg_number_reset =
10426 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10427 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10428 action_type == CA_SET_LEVEL_TIME ? level.time :
10429 action_type == CA_SET_LEVEL_SCORE ? 0 :
10430 #if USE_NEW_CUSTOM_VALUE
10431 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10433 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10435 action_type == CA_SET_CE_SCORE ? 0 :
10438 int action_arg_number =
10439 (action_arg <= CA_ARG_MAX ? action_arg :
10440 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10441 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10442 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10443 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10444 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10445 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10446 #if USE_NEW_CUSTOM_VALUE
10447 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10449 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10451 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10452 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10453 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10454 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10455 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10456 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10457 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10458 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10459 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10460 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10461 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10462 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10463 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10464 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10467 int action_arg_number_old =
10468 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10469 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10470 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10471 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10472 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10475 int action_arg_number_new =
10476 getModifiedActionNumber(action_arg_number_old,
10477 action_mode, action_arg_number,
10478 action_arg_number_min, action_arg_number_max);
10481 int trigger_player_bits =
10482 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10483 change->actual_trigger_player_bits : change->trigger_player);
10485 int trigger_player_bits =
10486 (change->actual_trigger_player >= EL_PLAYER_1 &&
10487 change->actual_trigger_player <= EL_PLAYER_4 ?
10488 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10492 int action_arg_player_bits =
10493 (action_arg >= CA_ARG_PLAYER_1 &&
10494 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10495 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10496 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10499 /* ---------- execute action -------------------------------------------- */
10501 switch (action_type)
10508 /* ---------- level actions ------------------------------------------- */
10510 case CA_RESTART_LEVEL:
10512 game.restart_level = TRUE;
10517 case CA_SHOW_ENVELOPE:
10519 int element = getSpecialActionElement(action_arg_element,
10520 action_arg_number, EL_ENVELOPE_1);
10522 if (IS_ENVELOPE(element))
10523 local_player->show_envelope = element;
10528 case CA_SET_LEVEL_TIME:
10530 if (level.time > 0) /* only modify limited time value */
10532 TimeLeft = action_arg_number_new;
10535 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10537 DisplayGameControlValues();
10539 DrawGameValue_Time(TimeLeft);
10542 if (!TimeLeft && setup.time_limit)
10543 for (i = 0; i < MAX_PLAYERS; i++)
10544 KillPlayer(&stored_player[i]);
10550 case CA_SET_LEVEL_SCORE:
10552 local_player->score = action_arg_number_new;
10555 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10557 DisplayGameControlValues();
10559 DrawGameValue_Score(local_player->score);
10565 case CA_SET_LEVEL_GEMS:
10567 local_player->gems_still_needed = action_arg_number_new;
10570 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10572 DisplayGameControlValues();
10574 DrawGameValue_Emeralds(local_player->gems_still_needed);
10580 #if !USE_PLAYER_GRAVITY
10581 case CA_SET_LEVEL_GRAVITY:
10583 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10584 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10585 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10591 case CA_SET_LEVEL_WIND:
10593 game.wind_direction = action_arg_direction;
10598 case CA_SET_LEVEL_RANDOM_SEED:
10601 /* ensure that setting a new random seed while playing is predictable */
10602 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10604 InitRND(action_arg_number_new);
10608 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10616 for (i = 0; i < 9; i++)
10617 printf("%d, ", RND(2));
10625 /* ---------- player actions ------------------------------------------ */
10627 case CA_MOVE_PLAYER:
10629 /* automatically move to the next field in specified direction */
10630 for (i = 0; i < MAX_PLAYERS; i++)
10631 if (trigger_player_bits & (1 << i))
10632 stored_player[i].programmed_action = action_arg_direction;
10637 case CA_EXIT_PLAYER:
10639 for (i = 0; i < MAX_PLAYERS; i++)
10640 if (action_arg_player_bits & (1 << i))
10641 PlayerWins(&stored_player[i]);
10646 case CA_KILL_PLAYER:
10648 for (i = 0; i < MAX_PLAYERS; i++)
10649 if (action_arg_player_bits & (1 << i))
10650 KillPlayer(&stored_player[i]);
10655 case CA_SET_PLAYER_KEYS:
10657 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10658 int element = getSpecialActionElement(action_arg_element,
10659 action_arg_number, EL_KEY_1);
10661 if (IS_KEY(element))
10663 for (i = 0; i < MAX_PLAYERS; i++)
10665 if (trigger_player_bits & (1 << i))
10667 stored_player[i].key[KEY_NR(element)] = key_state;
10669 DrawGameDoorValues();
10677 case CA_SET_PLAYER_SPEED:
10680 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10683 for (i = 0; i < MAX_PLAYERS; i++)
10685 if (trigger_player_bits & (1 << i))
10687 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10689 if (action_arg == CA_ARG_SPEED_FASTER &&
10690 stored_player[i].cannot_move)
10692 action_arg_number = STEPSIZE_VERY_SLOW;
10694 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10695 action_arg == CA_ARG_SPEED_FASTER)
10697 action_arg_number = 2;
10698 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10701 else if (action_arg == CA_ARG_NUMBER_RESET)
10703 action_arg_number = level.initial_player_stepsize[i];
10707 getModifiedActionNumber(move_stepsize,
10710 action_arg_number_min,
10711 action_arg_number_max);
10713 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10720 case CA_SET_PLAYER_SHIELD:
10722 for (i = 0; i < MAX_PLAYERS; i++)
10724 if (trigger_player_bits & (1 << i))
10726 if (action_arg == CA_ARG_SHIELD_OFF)
10728 stored_player[i].shield_normal_time_left = 0;
10729 stored_player[i].shield_deadly_time_left = 0;
10731 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10733 stored_player[i].shield_normal_time_left = 999999;
10735 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10737 stored_player[i].shield_normal_time_left = 999999;
10738 stored_player[i].shield_deadly_time_left = 999999;
10746 #if USE_PLAYER_GRAVITY
10747 case CA_SET_PLAYER_GRAVITY:
10749 for (i = 0; i < MAX_PLAYERS; i++)
10751 if (trigger_player_bits & (1 << i))
10753 stored_player[i].gravity =
10754 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10755 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10756 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10757 stored_player[i].gravity);
10765 case CA_SET_PLAYER_ARTWORK:
10767 for (i = 0; i < MAX_PLAYERS; i++)
10769 if (trigger_player_bits & (1 << i))
10771 int artwork_element = action_arg_element;
10773 if (action_arg == CA_ARG_ELEMENT_RESET)
10775 (level.use_artwork_element[i] ? level.artwork_element[i] :
10776 stored_player[i].element_nr);
10778 #if USE_GFX_RESET_PLAYER_ARTWORK
10779 if (stored_player[i].artwork_element != artwork_element)
10780 stored_player[i].Frame = 0;
10783 stored_player[i].artwork_element = artwork_element;
10785 SetPlayerWaiting(&stored_player[i], FALSE);
10787 /* set number of special actions for bored and sleeping animation */
10788 stored_player[i].num_special_action_bored =
10789 get_num_special_action(artwork_element,
10790 ACTION_BORING_1, ACTION_BORING_LAST);
10791 stored_player[i].num_special_action_sleeping =
10792 get_num_special_action(artwork_element,
10793 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10800 case CA_SET_PLAYER_INVENTORY:
10802 for (i = 0; i < MAX_PLAYERS; i++)
10804 struct PlayerInfo *player = &stored_player[i];
10807 if (trigger_player_bits & (1 << i))
10809 int inventory_element = action_arg_element;
10811 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10812 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10813 action_arg == CA_ARG_ELEMENT_ACTION)
10815 int element = inventory_element;
10816 int collect_count = element_info[element].collect_count_initial;
10818 if (!IS_CUSTOM_ELEMENT(element))
10821 if (collect_count == 0)
10822 player->inventory_infinite_element = element;
10824 for (k = 0; k < collect_count; k++)
10825 if (player->inventory_size < MAX_INVENTORY_SIZE)
10826 player->inventory_element[player->inventory_size++] =
10829 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10830 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10831 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10833 if (player->inventory_infinite_element != EL_UNDEFINED &&
10834 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10835 action_arg_element_raw))
10836 player->inventory_infinite_element = EL_UNDEFINED;
10838 for (k = 0, j = 0; j < player->inventory_size; j++)
10840 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10841 action_arg_element_raw))
10842 player->inventory_element[k++] = player->inventory_element[j];
10845 player->inventory_size = k;
10847 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10849 if (player->inventory_size > 0)
10851 for (j = 0; j < player->inventory_size - 1; j++)
10852 player->inventory_element[j] = player->inventory_element[j + 1];
10854 player->inventory_size--;
10857 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10859 if (player->inventory_size > 0)
10860 player->inventory_size--;
10862 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10864 player->inventory_infinite_element = EL_UNDEFINED;
10865 player->inventory_size = 0;
10867 else if (action_arg == CA_ARG_INVENTORY_RESET)
10869 player->inventory_infinite_element = EL_UNDEFINED;
10870 player->inventory_size = 0;
10872 if (level.use_initial_inventory[i])
10874 for (j = 0; j < level.initial_inventory_size[i]; j++)
10876 int element = level.initial_inventory_content[i][j];
10877 int collect_count = element_info[element].collect_count_initial;
10879 if (!IS_CUSTOM_ELEMENT(element))
10882 if (collect_count == 0)
10883 player->inventory_infinite_element = element;
10885 for (k = 0; k < collect_count; k++)
10886 if (player->inventory_size < MAX_INVENTORY_SIZE)
10887 player->inventory_element[player->inventory_size++] =
10898 /* ---------- CE actions ---------------------------------------------- */
10900 case CA_SET_CE_VALUE:
10902 #if USE_NEW_CUSTOM_VALUE
10903 int last_ce_value = CustomValue[x][y];
10905 CustomValue[x][y] = action_arg_number_new;
10907 if (CustomValue[x][y] != last_ce_value)
10909 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10910 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10912 if (CustomValue[x][y] == 0)
10914 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10915 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10923 case CA_SET_CE_SCORE:
10925 #if USE_NEW_CUSTOM_VALUE
10926 int last_ce_score = ei->collect_score;
10928 ei->collect_score = action_arg_number_new;
10930 if (ei->collect_score != last_ce_score)
10932 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10933 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10935 if (ei->collect_score == 0)
10939 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10940 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10943 This is a very special case that seems to be a mixture between
10944 CheckElementChange() and CheckTriggeredElementChange(): while
10945 the first one only affects single elements that are triggered
10946 directly, the second one affects multiple elements in the playfield
10947 that are triggered indirectly by another element. This is a third
10948 case: Changing the CE score always affects multiple identical CEs,
10949 so every affected CE must be checked, not only the single CE for
10950 which the CE score was changed in the first place (as every instance
10951 of that CE shares the same CE score, and therefore also can change)!
10953 SCAN_PLAYFIELD(xx, yy)
10955 if (Feld[xx][yy] == element)
10956 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10957 CE_SCORE_GETS_ZERO);
10966 case CA_SET_CE_ARTWORK:
10968 int artwork_element = action_arg_element;
10969 boolean reset_frame = FALSE;
10972 if (action_arg == CA_ARG_ELEMENT_RESET)
10973 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10976 if (ei->gfx_element != artwork_element)
10977 reset_frame = TRUE;
10979 ei->gfx_element = artwork_element;
10981 SCAN_PLAYFIELD(xx, yy)
10983 if (Feld[xx][yy] == element)
10987 ResetGfxAnimation(xx, yy);
10988 ResetRandomAnimationValue(xx, yy);
10991 TEST_DrawLevelField(xx, yy);
10998 /* ---------- engine actions ------------------------------------------ */
11000 case CA_SET_ENGINE_SCAN_MODE:
11002 InitPlayfieldScanMode(action_arg);
11012 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11014 int old_element = Feld[x][y];
11015 int new_element = GetElementFromGroupElement(element);
11016 int previous_move_direction = MovDir[x][y];
11017 #if USE_NEW_CUSTOM_VALUE
11018 int last_ce_value = CustomValue[x][y];
11020 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11021 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11022 boolean add_player_onto_element = (new_element_is_player &&
11023 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11024 /* this breaks SnakeBite when a snake is
11025 halfway through a door that closes */
11026 /* NOW FIXED AT LEVEL INIT IN files.c */
11027 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11029 IS_WALKABLE(old_element));
11032 /* check if element under the player changes from accessible to unaccessible
11033 (needed for special case of dropping element which then changes) */
11034 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11035 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11043 if (!add_player_onto_element)
11045 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11046 RemoveMovingField(x, y);
11050 Feld[x][y] = new_element;
11052 #if !USE_GFX_RESET_GFX_ANIMATION
11053 ResetGfxAnimation(x, y);
11054 ResetRandomAnimationValue(x, y);
11057 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11058 MovDir[x][y] = previous_move_direction;
11060 #if USE_NEW_CUSTOM_VALUE
11061 if (element_info[new_element].use_last_ce_value)
11062 CustomValue[x][y] = last_ce_value;
11065 InitField_WithBug1(x, y, FALSE);
11067 new_element = Feld[x][y]; /* element may have changed */
11069 #if USE_GFX_RESET_GFX_ANIMATION
11070 ResetGfxAnimation(x, y);
11071 ResetRandomAnimationValue(x, y);
11074 TEST_DrawLevelField(x, y);
11076 if (GFX_CRUMBLED(new_element))
11077 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11081 /* check if element under the player changes from accessible to unaccessible
11082 (needed for special case of dropping element which then changes) */
11083 /* (must be checked after creating new element for walkable group elements) */
11084 #if USE_FIX_KILLED_BY_NON_WALKABLE
11085 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11086 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11093 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11094 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11103 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11104 if (new_element_is_player)
11105 RelocatePlayer(x, y, new_element);
11108 ChangeCount[x][y]++; /* count number of changes in the same frame */
11110 TestIfBadThingTouchesPlayer(x, y);
11111 TestIfPlayerTouchesCustomElement(x, y);
11112 TestIfElementTouchesCustomElement(x, y);
11115 static void CreateField(int x, int y, int element)
11117 CreateFieldExt(x, y, element, FALSE);
11120 static void CreateElementFromChange(int x, int y, int element)
11122 element = GET_VALID_RUNTIME_ELEMENT(element);
11124 #if USE_STOP_CHANGED_ELEMENTS
11125 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11127 int old_element = Feld[x][y];
11129 /* prevent changed element from moving in same engine frame
11130 unless both old and new element can either fall or move */
11131 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11132 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11137 CreateFieldExt(x, y, element, TRUE);
11140 static boolean ChangeElement(int x, int y, int element, int page)
11142 struct ElementInfo *ei = &element_info[element];
11143 struct ElementChangeInfo *change = &ei->change_page[page];
11144 int ce_value = CustomValue[x][y];
11145 int ce_score = ei->collect_score;
11146 int target_element;
11147 int old_element = Feld[x][y];
11149 /* always use default change event to prevent running into a loop */
11150 if (ChangeEvent[x][y] == -1)
11151 ChangeEvent[x][y] = CE_DELAY;
11153 if (ChangeEvent[x][y] == CE_DELAY)
11155 /* reset actual trigger element, trigger player and action element */
11156 change->actual_trigger_element = EL_EMPTY;
11157 change->actual_trigger_player = EL_EMPTY;
11158 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11159 change->actual_trigger_side = CH_SIDE_NONE;
11160 change->actual_trigger_ce_value = 0;
11161 change->actual_trigger_ce_score = 0;
11164 /* do not change elements more than a specified maximum number of changes */
11165 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11168 ChangeCount[x][y]++; /* count number of changes in the same frame */
11170 if (change->explode)
11177 if (change->use_target_content)
11179 boolean complete_replace = TRUE;
11180 boolean can_replace[3][3];
11183 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11186 boolean is_walkable;
11187 boolean is_diggable;
11188 boolean is_collectible;
11189 boolean is_removable;
11190 boolean is_destructible;
11191 int ex = x + xx - 1;
11192 int ey = y + yy - 1;
11193 int content_element = change->target_content.e[xx][yy];
11196 can_replace[xx][yy] = TRUE;
11198 if (ex == x && ey == y) /* do not check changing element itself */
11201 if (content_element == EL_EMPTY_SPACE)
11203 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11208 if (!IN_LEV_FIELD(ex, ey))
11210 can_replace[xx][yy] = FALSE;
11211 complete_replace = FALSE;
11218 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11219 e = MovingOrBlocked2Element(ex, ey);
11221 is_empty = (IS_FREE(ex, ey) ||
11222 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11224 is_walkable = (is_empty || IS_WALKABLE(e));
11225 is_diggable = (is_empty || IS_DIGGABLE(e));
11226 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11227 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11228 is_removable = (is_diggable || is_collectible);
11230 can_replace[xx][yy] =
11231 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11232 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11233 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11234 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11235 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11236 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11237 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11239 if (!can_replace[xx][yy])
11240 complete_replace = FALSE;
11243 if (!change->only_if_complete || complete_replace)
11245 boolean something_has_changed = FALSE;
11247 if (change->only_if_complete && change->use_random_replace &&
11248 RND(100) < change->random_percentage)
11251 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11253 int ex = x + xx - 1;
11254 int ey = y + yy - 1;
11255 int content_element;
11257 if (can_replace[xx][yy] && (!change->use_random_replace ||
11258 RND(100) < change->random_percentage))
11260 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11261 RemoveMovingField(ex, ey);
11263 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11265 content_element = change->target_content.e[xx][yy];
11266 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11267 ce_value, ce_score);
11269 CreateElementFromChange(ex, ey, target_element);
11271 something_has_changed = TRUE;
11273 /* for symmetry reasons, freeze newly created border elements */
11274 if (ex != x || ey != y)
11275 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11279 if (something_has_changed)
11281 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11282 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11288 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11289 ce_value, ce_score);
11291 if (element == EL_DIAGONAL_GROWING ||
11292 element == EL_DIAGONAL_SHRINKING)
11294 target_element = Store[x][y];
11296 Store[x][y] = EL_EMPTY;
11299 CreateElementFromChange(x, y, target_element);
11301 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11302 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11305 /* this uses direct change before indirect change */
11306 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11311 #if USE_NEW_DELAYED_ACTION
11313 static void HandleElementChange(int x, int y, int page)
11315 int element = MovingOrBlocked2Element(x, y);
11316 struct ElementInfo *ei = &element_info[element];
11317 struct ElementChangeInfo *change = &ei->change_page[page];
11318 boolean handle_action_before_change = FALSE;
11321 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11322 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11325 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11326 x, y, element, element_info[element].token_name);
11327 printf("HandleElementChange(): This should never happen!\n");
11332 /* this can happen with classic bombs on walkable, changing elements */
11333 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11336 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11337 ChangeDelay[x][y] = 0;
11343 if (ChangeDelay[x][y] == 0) /* initialize element change */
11345 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11347 if (change->can_change)
11350 /* !!! not clear why graphic animation should be reset at all here !!! */
11351 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11352 #if USE_GFX_RESET_WHEN_NOT_MOVING
11353 /* when a custom element is about to change (for example by change delay),
11354 do not reset graphic animation when the custom element is moving */
11355 if (!IS_MOVING(x, y))
11358 ResetGfxAnimation(x, y);
11359 ResetRandomAnimationValue(x, y);
11363 if (change->pre_change_function)
11364 change->pre_change_function(x, y);
11368 ChangeDelay[x][y]--;
11370 if (ChangeDelay[x][y] != 0) /* continue element change */
11372 if (change->can_change)
11374 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11376 if (IS_ANIMATED(graphic))
11377 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11379 if (change->change_function)
11380 change->change_function(x, y);
11383 else /* finish element change */
11385 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11387 page = ChangePage[x][y];
11388 ChangePage[x][y] = -1;
11390 change = &ei->change_page[page];
11393 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11395 ChangeDelay[x][y] = 1; /* try change after next move step */
11396 ChangePage[x][y] = page; /* remember page to use for change */
11402 /* special case: set new level random seed before changing element */
11403 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11404 handle_action_before_change = TRUE;
11406 if (change->has_action && handle_action_before_change)
11407 ExecuteCustomElementAction(x, y, element, page);
11410 if (change->can_change)
11412 if (ChangeElement(x, y, element, page))
11414 if (change->post_change_function)
11415 change->post_change_function(x, y);
11419 if (change->has_action && !handle_action_before_change)
11420 ExecuteCustomElementAction(x, y, element, page);
11426 static void HandleElementChange(int x, int y, int page)
11428 int element = MovingOrBlocked2Element(x, y);
11429 struct ElementInfo *ei = &element_info[element];
11430 struct ElementChangeInfo *change = &ei->change_page[page];
11433 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11436 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11437 x, y, element, element_info[element].token_name);
11438 printf("HandleElementChange(): This should never happen!\n");
11443 /* this can happen with classic bombs on walkable, changing elements */
11444 if (!CAN_CHANGE(element))
11447 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11448 ChangeDelay[x][y] = 0;
11454 if (ChangeDelay[x][y] == 0) /* initialize element change */
11456 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11458 ResetGfxAnimation(x, y);
11459 ResetRandomAnimationValue(x, y);
11461 if (change->pre_change_function)
11462 change->pre_change_function(x, y);
11465 ChangeDelay[x][y]--;
11467 if (ChangeDelay[x][y] != 0) /* continue element change */
11469 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11471 if (IS_ANIMATED(graphic))
11472 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474 if (change->change_function)
11475 change->change_function(x, y);
11477 else /* finish element change */
11479 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11481 page = ChangePage[x][y];
11482 ChangePage[x][y] = -1;
11484 change = &ei->change_page[page];
11487 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11489 ChangeDelay[x][y] = 1; /* try change after next move step */
11490 ChangePage[x][y] = page; /* remember page to use for change */
11495 if (ChangeElement(x, y, element, page))
11497 if (change->post_change_function)
11498 change->post_change_function(x, y);
11505 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11506 int trigger_element,
11508 int trigger_player,
11512 boolean change_done_any = FALSE;
11513 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11516 if (!(trigger_events[trigger_element][trigger_event]))
11520 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11521 trigger_event, recursion_loop_depth, recursion_loop_detected,
11522 recursion_loop_element, EL_NAME(recursion_loop_element));
11525 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11527 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11529 int element = EL_CUSTOM_START + i;
11530 boolean change_done = FALSE;
11533 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11534 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11537 for (p = 0; p < element_info[element].num_change_pages; p++)
11539 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11541 if (change->can_change_or_has_action &&
11542 change->has_event[trigger_event] &&
11543 change->trigger_side & trigger_side &&
11544 change->trigger_player & trigger_player &&
11545 change->trigger_page & trigger_page_bits &&
11546 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11548 change->actual_trigger_element = trigger_element;
11549 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11550 change->actual_trigger_player_bits = trigger_player;
11551 change->actual_trigger_side = trigger_side;
11552 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11553 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11556 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11557 element, EL_NAME(element), p);
11560 if ((change->can_change && !change_done) || change->has_action)
11564 SCAN_PLAYFIELD(x, y)
11566 if (Feld[x][y] == element)
11568 if (change->can_change && !change_done)
11570 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11571 /* if element already changed in this frame, not only prevent
11572 another element change (checked in ChangeElement()), but
11573 also prevent additional element actions for this element */
11575 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11576 !level.use_action_after_change_bug)
11581 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11582 element, EL_NAME(element), p);
11585 ChangeDelay[x][y] = 1;
11586 ChangeEvent[x][y] = trigger_event;
11588 HandleElementChange(x, y, p);
11590 #if USE_NEW_DELAYED_ACTION
11591 else if (change->has_action)
11593 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11594 /* if element already changed in this frame, not only prevent
11595 another element change (checked in ChangeElement()), but
11596 also prevent additional element actions for this element */
11598 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11599 !level.use_action_after_change_bug)
11605 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11606 element, EL_NAME(element), p);
11609 ExecuteCustomElementAction(x, y, element, p);
11610 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11613 if (change->has_action)
11615 ExecuteCustomElementAction(x, y, element, p);
11616 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11622 if (change->can_change)
11624 change_done = TRUE;
11625 change_done_any = TRUE;
11628 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11629 element, EL_NAME(element), p);
11638 RECURSION_LOOP_DETECTION_END();
11640 return change_done_any;
11643 static boolean CheckElementChangeExt(int x, int y,
11645 int trigger_element,
11647 int trigger_player,
11650 boolean change_done = FALSE;
11653 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11654 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11657 if (Feld[x][y] == EL_BLOCKED)
11659 Blocked2Moving(x, y, &x, &y);
11660 element = Feld[x][y];
11664 /* check if element has already changed */
11665 if (Feld[x][y] != element)
11668 /* check if element has already changed or is about to change after moving */
11669 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11670 Feld[x][y] != element) ||
11672 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11673 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11674 ChangePage[x][y] != -1)))
11679 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11680 trigger_event, recursion_loop_depth, recursion_loop_detected,
11681 recursion_loop_element, EL_NAME(recursion_loop_element));
11684 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11687 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11690 for (p = 0; p < element_info[element].num_change_pages; p++)
11692 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11694 /* check trigger element for all events where the element that is checked
11695 for changing interacts with a directly adjacent element -- this is
11696 different to element changes that affect other elements to change on the
11697 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11698 boolean check_trigger_element =
11699 (trigger_event == CE_TOUCHING_X ||
11700 trigger_event == CE_HITTING_X ||
11701 trigger_event == CE_HIT_BY_X ||
11703 /* this one was forgotten until 3.2.3 */
11704 trigger_event == CE_DIGGING_X);
11707 if (change->can_change_or_has_action &&
11708 change->has_event[trigger_event] &&
11709 change->trigger_side & trigger_side &&
11710 change->trigger_player & trigger_player &&
11711 (!check_trigger_element ||
11712 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11714 change->actual_trigger_element = trigger_element;
11715 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11716 change->actual_trigger_player_bits = trigger_player;
11717 change->actual_trigger_side = trigger_side;
11718 change->actual_trigger_ce_value = CustomValue[x][y];
11719 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11721 /* special case: trigger element not at (x,y) position for some events */
11722 if (check_trigger_element)
11734 { 0, 0 }, { 0, 0 }, { 0, 0 },
11738 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11739 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11741 change->actual_trigger_ce_value = CustomValue[xx][yy];
11742 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11745 if (change->can_change && !change_done)
11747 ChangeDelay[x][y] = 1;
11748 ChangeEvent[x][y] = trigger_event;
11750 HandleElementChange(x, y, p);
11752 change_done = TRUE;
11754 #if USE_NEW_DELAYED_ACTION
11755 else if (change->has_action)
11757 ExecuteCustomElementAction(x, y, element, p);
11758 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11761 if (change->has_action)
11763 ExecuteCustomElementAction(x, y, element, p);
11764 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11770 RECURSION_LOOP_DETECTION_END();
11772 return change_done;
11775 static void PlayPlayerSound(struct PlayerInfo *player)
11777 int jx = player->jx, jy = player->jy;
11778 int sound_element = player->artwork_element;
11779 int last_action = player->last_action_waiting;
11780 int action = player->action_waiting;
11782 if (player->is_waiting)
11784 if (action != last_action)
11785 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11787 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11791 if (action != last_action)
11792 StopSound(element_info[sound_element].sound[last_action]);
11794 if (last_action == ACTION_SLEEPING)
11795 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11799 static void PlayAllPlayersSound()
11803 for (i = 0; i < MAX_PLAYERS; i++)
11804 if (stored_player[i].active)
11805 PlayPlayerSound(&stored_player[i]);
11808 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11810 boolean last_waiting = player->is_waiting;
11811 int move_dir = player->MovDir;
11813 player->dir_waiting = move_dir;
11814 player->last_action_waiting = player->action_waiting;
11818 if (!last_waiting) /* not waiting -> waiting */
11820 player->is_waiting = TRUE;
11822 player->frame_counter_bored =
11824 game.player_boring_delay_fixed +
11825 GetSimpleRandom(game.player_boring_delay_random);
11826 player->frame_counter_sleeping =
11828 game.player_sleeping_delay_fixed +
11829 GetSimpleRandom(game.player_sleeping_delay_random);
11831 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11834 if (game.player_sleeping_delay_fixed +
11835 game.player_sleeping_delay_random > 0 &&
11836 player->anim_delay_counter == 0 &&
11837 player->post_delay_counter == 0 &&
11838 FrameCounter >= player->frame_counter_sleeping)
11839 player->is_sleeping = TRUE;
11840 else if (game.player_boring_delay_fixed +
11841 game.player_boring_delay_random > 0 &&
11842 FrameCounter >= player->frame_counter_bored)
11843 player->is_bored = TRUE;
11845 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11846 player->is_bored ? ACTION_BORING :
11849 if (player->is_sleeping && player->use_murphy)
11851 /* special case for sleeping Murphy when leaning against non-free tile */
11853 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11854 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11855 !IS_MOVING(player->jx - 1, player->jy)))
11856 move_dir = MV_LEFT;
11857 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11858 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11859 !IS_MOVING(player->jx + 1, player->jy)))
11860 move_dir = MV_RIGHT;
11862 player->is_sleeping = FALSE;
11864 player->dir_waiting = move_dir;
11867 if (player->is_sleeping)
11869 if (player->num_special_action_sleeping > 0)
11871 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11873 int last_special_action = player->special_action_sleeping;
11874 int num_special_action = player->num_special_action_sleeping;
11875 int special_action =
11876 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11877 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11878 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11879 last_special_action + 1 : ACTION_SLEEPING);
11880 int special_graphic =
11881 el_act_dir2img(player->artwork_element, special_action, move_dir);
11883 player->anim_delay_counter =
11884 graphic_info[special_graphic].anim_delay_fixed +
11885 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11886 player->post_delay_counter =
11887 graphic_info[special_graphic].post_delay_fixed +
11888 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11890 player->special_action_sleeping = special_action;
11893 if (player->anim_delay_counter > 0)
11895 player->action_waiting = player->special_action_sleeping;
11896 player->anim_delay_counter--;
11898 else if (player->post_delay_counter > 0)
11900 player->post_delay_counter--;
11904 else if (player->is_bored)
11906 if (player->num_special_action_bored > 0)
11908 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11910 int special_action =
11911 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11912 int special_graphic =
11913 el_act_dir2img(player->artwork_element, special_action, move_dir);
11915 player->anim_delay_counter =
11916 graphic_info[special_graphic].anim_delay_fixed +
11917 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11918 player->post_delay_counter =
11919 graphic_info[special_graphic].post_delay_fixed +
11920 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11922 player->special_action_bored = special_action;
11925 if (player->anim_delay_counter > 0)
11927 player->action_waiting = player->special_action_bored;
11928 player->anim_delay_counter--;
11930 else if (player->post_delay_counter > 0)
11932 player->post_delay_counter--;
11937 else if (last_waiting) /* waiting -> not waiting */
11939 player->is_waiting = FALSE;
11940 player->is_bored = FALSE;
11941 player->is_sleeping = FALSE;
11943 player->frame_counter_bored = -1;
11944 player->frame_counter_sleeping = -1;
11946 player->anim_delay_counter = 0;
11947 player->post_delay_counter = 0;
11949 player->dir_waiting = player->MovDir;
11950 player->action_waiting = ACTION_DEFAULT;
11952 player->special_action_bored = ACTION_DEFAULT;
11953 player->special_action_sleeping = ACTION_DEFAULT;
11957 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11959 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11960 int left = player_action & JOY_LEFT;
11961 int right = player_action & JOY_RIGHT;
11962 int up = player_action & JOY_UP;
11963 int down = player_action & JOY_DOWN;
11964 int button1 = player_action & JOY_BUTTON_1;
11965 int button2 = player_action & JOY_BUTTON_2;
11966 int dx = (left ? -1 : right ? 1 : 0);
11967 int dy = (up ? -1 : down ? 1 : 0);
11969 if (!player->active || tape.pausing)
11975 snapped = SnapField(player, dx, dy);
11979 dropped = DropElement(player);
11981 moved = MovePlayer(player, dx, dy);
11984 if (tape.single_step && tape.recording && !tape.pausing)
11986 if (button1 || (dropped && !moved))
11988 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11989 SnapField(player, 0, 0); /* stop snapping */
11993 SetPlayerWaiting(player, FALSE);
11995 return player_action;
11999 /* no actions for this player (no input at player's configured device) */
12001 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12002 SnapField(player, 0, 0);
12003 CheckGravityMovementWhenNotMoving(player);
12005 if (player->MovPos == 0)
12006 SetPlayerWaiting(player, TRUE);
12008 if (player->MovPos == 0) /* needed for tape.playing */
12009 player->is_moving = FALSE;
12011 player->is_dropping = FALSE;
12012 player->is_dropping_pressed = FALSE;
12013 player->drop_pressed_delay = 0;
12019 static void CheckLevelTime()
12023 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12025 if (level.native_em_level->lev->home == 0) /* all players at home */
12027 PlayerWins(local_player);
12029 AllPlayersGone = TRUE;
12031 level.native_em_level->lev->home = -1;
12034 if (level.native_em_level->ply[0]->alive == 0 &&
12035 level.native_em_level->ply[1]->alive == 0 &&
12036 level.native_em_level->ply[2]->alive == 0 &&
12037 level.native_em_level->ply[3]->alive == 0) /* all dead */
12038 AllPlayersGone = TRUE;
12041 if (TimeFrames >= FRAMES_PER_SECOND)
12046 for (i = 0; i < MAX_PLAYERS; i++)
12048 struct PlayerInfo *player = &stored_player[i];
12050 if (SHIELD_ON(player))
12052 player->shield_normal_time_left--;
12054 if (player->shield_deadly_time_left > 0)
12055 player->shield_deadly_time_left--;
12059 if (!local_player->LevelSolved && !level.use_step_counter)
12067 if (TimeLeft <= 10 && setup.time_limit)
12068 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12071 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12073 DisplayGameControlValues();
12075 DrawGameValue_Time(TimeLeft);
12078 if (!TimeLeft && setup.time_limit)
12080 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12081 level.native_em_level->lev->killed_out_of_time = TRUE;
12083 for (i = 0; i < MAX_PLAYERS; i++)
12084 KillPlayer(&stored_player[i]);
12088 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12090 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12092 DisplayGameControlValues();
12095 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12096 DrawGameValue_Time(TimePlayed);
12099 level.native_em_level->lev->time =
12100 (level.time == 0 ? TimePlayed : TimeLeft);
12103 if (tape.recording || tape.playing)
12104 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12108 UpdateAndDisplayGameControlValues();
12110 UpdateGameDoorValues();
12111 DrawGameDoorValues();
12115 void AdvanceFrameAndPlayerCounters(int player_nr)
12119 /* advance frame counters (global frame counter and time frame counter) */
12123 /* advance player counters (counters for move delay, move animation etc.) */
12124 for (i = 0; i < MAX_PLAYERS; i++)
12126 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12127 int move_delay_value = stored_player[i].move_delay_value;
12128 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12130 if (!advance_player_counters) /* not all players may be affected */
12133 #if USE_NEW_PLAYER_ANIM
12134 if (move_frames == 0) /* less than one move per game frame */
12136 int stepsize = TILEX / move_delay_value;
12137 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12138 int count = (stored_player[i].is_moving ?
12139 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12141 if (count % delay == 0)
12146 stored_player[i].Frame += move_frames;
12148 if (stored_player[i].MovPos != 0)
12149 stored_player[i].StepFrame += move_frames;
12151 if (stored_player[i].move_delay > 0)
12152 stored_player[i].move_delay--;
12154 /* due to bugs in previous versions, counter must count up, not down */
12155 if (stored_player[i].push_delay != -1)
12156 stored_player[i].push_delay++;
12158 if (stored_player[i].drop_delay > 0)
12159 stored_player[i].drop_delay--;
12161 if (stored_player[i].is_dropping_pressed)
12162 stored_player[i].drop_pressed_delay++;
12166 void StartGameActions(boolean init_network_game, boolean record_tape,
12169 unsigned long new_random_seed = InitRND(random_seed);
12172 TapeStartRecording(new_random_seed);
12174 #if defined(NETWORK_AVALIABLE)
12175 if (init_network_game)
12177 SendToServer_StartPlaying();
12188 static unsigned long game_frame_delay = 0;
12189 unsigned long game_frame_delay_value;
12190 byte *recorded_player_action;
12191 byte summarized_player_action = 0;
12192 byte tape_action[MAX_PLAYERS];
12195 /* detect endless loops, caused by custom element programming */
12196 if (recursion_loop_detected && recursion_loop_depth == 0)
12198 char *message = getStringCat3("Internal Error ! Element ",
12199 EL_NAME(recursion_loop_element),
12200 " caused endless loop ! Quit the game ?");
12202 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12203 EL_NAME(recursion_loop_element));
12205 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12207 recursion_loop_detected = FALSE; /* if game should be continued */
12214 if (game.restart_level)
12215 StartGameActions(options.network, setup.autorecord, level.random_seed);
12217 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12219 if (level.native_em_level->lev->home == 0) /* all players at home */
12221 PlayerWins(local_player);
12223 AllPlayersGone = TRUE;
12225 level.native_em_level->lev->home = -1;
12228 if (level.native_em_level->ply[0]->alive == 0 &&
12229 level.native_em_level->ply[1]->alive == 0 &&
12230 level.native_em_level->ply[2]->alive == 0 &&
12231 level.native_em_level->ply[3]->alive == 0) /* all dead */
12232 AllPlayersGone = TRUE;
12235 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12238 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12241 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12244 game_frame_delay_value =
12245 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12247 if (tape.playing && tape.warp_forward && !tape.pausing)
12248 game_frame_delay_value = 0;
12250 /* ---------- main game synchronization point ---------- */
12252 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12254 if (network_playing && !network_player_action_received)
12256 /* try to get network player actions in time */
12258 #if defined(NETWORK_AVALIABLE)
12259 /* last chance to get network player actions without main loop delay */
12260 HandleNetworking();
12263 /* game was quit by network peer */
12264 if (game_status != GAME_MODE_PLAYING)
12267 if (!network_player_action_received)
12268 return; /* failed to get network player actions in time */
12270 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12276 /* at this point we know that we really continue executing the game */
12278 network_player_action_received = FALSE;
12280 /* when playing tape, read previously recorded player input from tape data */
12281 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12284 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12289 if (tape.set_centered_player)
12291 game.centered_player_nr_next = tape.centered_player_nr_next;
12292 game.set_centered_player = TRUE;
12295 for (i = 0; i < MAX_PLAYERS; i++)
12297 summarized_player_action |= stored_player[i].action;
12299 if (!network_playing)
12300 stored_player[i].effective_action = stored_player[i].action;
12303 #if defined(NETWORK_AVALIABLE)
12304 if (network_playing)
12305 SendToServer_MovePlayer(summarized_player_action);
12308 if (!options.network && !setup.team_mode)
12309 local_player->effective_action = summarized_player_action;
12311 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12313 for (i = 0; i < MAX_PLAYERS; i++)
12314 stored_player[i].effective_action =
12315 (i == game.centered_player_nr ? summarized_player_action : 0);
12318 if (recorded_player_action != NULL)
12319 for (i = 0; i < MAX_PLAYERS; i++)
12320 stored_player[i].effective_action = recorded_player_action[i];
12322 for (i = 0; i < MAX_PLAYERS; i++)
12324 tape_action[i] = stored_player[i].effective_action;
12326 /* (this can only happen in the R'n'D game engine) */
12327 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12328 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12331 /* only record actions from input devices, but not programmed actions */
12332 if (tape.recording)
12333 TapeRecordAction(tape_action);
12335 #if USE_NEW_PLAYER_ASSIGNMENTS
12337 byte mapped_action[MAX_PLAYERS];
12339 for (i = 0; i < MAX_PLAYERS; i++)
12340 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12342 for (i = 0; i < MAX_PLAYERS; i++)
12343 stored_player[i].effective_action = mapped_action[i];
12347 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12349 GameActions_EM_Main();
12351 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12353 GameActions_SP_Main();
12361 void GameActions_EM_Main()
12363 byte effective_action[MAX_PLAYERS];
12364 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12367 for (i = 0; i < MAX_PLAYERS; i++)
12368 effective_action[i] = stored_player[i].effective_action;
12370 GameActions_EM(effective_action, warp_mode);
12374 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12377 void GameActions_SP_Main()
12379 byte effective_action[MAX_PLAYERS];
12380 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12383 for (i = 0; i < MAX_PLAYERS; i++)
12384 effective_action[i] = stored_player[i].effective_action;
12386 GameActions_SP(effective_action, warp_mode);
12390 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12393 void GameActions_RND()
12395 int magic_wall_x = 0, magic_wall_y = 0;
12396 int i, x, y, element, graphic;
12398 InitPlayfieldScanModeVars();
12400 #if USE_ONE_MORE_CHANGE_PER_FRAME
12401 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12403 SCAN_PLAYFIELD(x, y)
12405 ChangeCount[x][y] = 0;
12406 ChangeEvent[x][y] = -1;
12411 if (game.set_centered_player)
12413 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12415 /* switching to "all players" only possible if all players fit to screen */
12416 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12418 game.centered_player_nr_next = game.centered_player_nr;
12419 game.set_centered_player = FALSE;
12422 /* do not switch focus to non-existing (or non-active) player */
12423 if (game.centered_player_nr_next >= 0 &&
12424 !stored_player[game.centered_player_nr_next].active)
12426 game.centered_player_nr_next = game.centered_player_nr;
12427 game.set_centered_player = FALSE;
12431 if (game.set_centered_player &&
12432 ScreenMovPos == 0) /* screen currently aligned at tile position */
12436 if (game.centered_player_nr_next == -1)
12438 setScreenCenteredToAllPlayers(&sx, &sy);
12442 sx = stored_player[game.centered_player_nr_next].jx;
12443 sy = stored_player[game.centered_player_nr_next].jy;
12446 game.centered_player_nr = game.centered_player_nr_next;
12447 game.set_centered_player = FALSE;
12449 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12450 DrawGameDoorValues();
12453 for (i = 0; i < MAX_PLAYERS; i++)
12455 int actual_player_action = stored_player[i].effective_action;
12458 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12459 - rnd_equinox_tetrachloride 048
12460 - rnd_equinox_tetrachloride_ii 096
12461 - rnd_emanuel_schmieg 002
12462 - doctor_sloan_ww 001, 020
12464 if (stored_player[i].MovPos == 0)
12465 CheckGravityMovement(&stored_player[i]);
12468 /* overwrite programmed action with tape action */
12469 if (stored_player[i].programmed_action)
12470 actual_player_action = stored_player[i].programmed_action;
12472 PlayerActions(&stored_player[i], actual_player_action);
12474 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12477 ScrollScreen(NULL, SCROLL_GO_ON);
12479 /* for backwards compatibility, the following code emulates a fixed bug that
12480 occured when pushing elements (causing elements that just made their last
12481 pushing step to already (if possible) make their first falling step in the
12482 same game frame, which is bad); this code is also needed to use the famous
12483 "spring push bug" which is used in older levels and might be wanted to be
12484 used also in newer levels, but in this case the buggy pushing code is only
12485 affecting the "spring" element and no other elements */
12487 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12489 for (i = 0; i < MAX_PLAYERS; i++)
12491 struct PlayerInfo *player = &stored_player[i];
12492 int x = player->jx;
12493 int y = player->jy;
12495 if (player->active && player->is_pushing && player->is_moving &&
12497 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12498 Feld[x][y] == EL_SPRING))
12500 ContinueMoving(x, y);
12502 /* continue moving after pushing (this is actually a bug) */
12503 if (!IS_MOVING(x, y))
12504 Stop[x][y] = FALSE;
12510 debug_print_timestamp(0, "start main loop profiling");
12513 SCAN_PLAYFIELD(x, y)
12515 ChangeCount[x][y] = 0;
12516 ChangeEvent[x][y] = -1;
12518 /* this must be handled before main playfield loop */
12519 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12522 if (MovDelay[x][y] <= 0)
12526 #if USE_NEW_SNAP_DELAY
12527 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12530 if (MovDelay[x][y] <= 0)
12533 TEST_DrawLevelField(x, y);
12535 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12541 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12543 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12544 printf("GameActions(): This should never happen!\n");
12546 ChangePage[x][y] = -1;
12550 Stop[x][y] = FALSE;
12551 if (WasJustMoving[x][y] > 0)
12552 WasJustMoving[x][y]--;
12553 if (WasJustFalling[x][y] > 0)
12554 WasJustFalling[x][y]--;
12555 if (CheckCollision[x][y] > 0)
12556 CheckCollision[x][y]--;
12557 if (CheckImpact[x][y] > 0)
12558 CheckImpact[x][y]--;
12562 /* reset finished pushing action (not done in ContinueMoving() to allow
12563 continuous pushing animation for elements with zero push delay) */
12564 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12566 ResetGfxAnimation(x, y);
12567 TEST_DrawLevelField(x, y);
12571 if (IS_BLOCKED(x, y))
12575 Blocked2Moving(x, y, &oldx, &oldy);
12576 if (!IS_MOVING(oldx, oldy))
12578 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12579 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12580 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12581 printf("GameActions(): This should never happen!\n");
12588 debug_print_timestamp(0, "- time for pre-main loop:");
12591 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12592 SCAN_PLAYFIELD(x, y)
12594 element = Feld[x][y];
12595 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12600 int element2 = element;
12601 int graphic2 = graphic;
12603 int element2 = Feld[x][y];
12604 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12606 int last_gfx_frame = GfxFrame[x][y];
12608 if (graphic_info[graphic2].anim_global_sync)
12609 GfxFrame[x][y] = FrameCounter;
12610 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12611 GfxFrame[x][y] = CustomValue[x][y];
12612 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12613 GfxFrame[x][y] = element_info[element2].collect_score;
12614 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12615 GfxFrame[x][y] = ChangeDelay[x][y];
12617 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12618 DrawLevelGraphicAnimation(x, y, graphic2);
12621 ResetGfxFrame(x, y, TRUE);
12625 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12626 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12627 ResetRandomAnimationValue(x, y);
12631 SetRandomAnimationValue(x, y);
12635 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12638 #endif // -------------------- !!! TEST ONLY !!! --------------------
12641 debug_print_timestamp(0, "- time for TEST loop: -->");
12644 SCAN_PLAYFIELD(x, y)
12646 element = Feld[x][y];
12647 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12649 ResetGfxFrame(x, y, TRUE);
12651 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12652 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12653 ResetRandomAnimationValue(x, y);
12655 SetRandomAnimationValue(x, y);
12657 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12659 if (IS_INACTIVE(element))
12661 if (IS_ANIMATED(graphic))
12662 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12667 /* this may take place after moving, so 'element' may have changed */
12668 if (IS_CHANGING(x, y) &&
12669 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12671 int page = element_info[element].event_page_nr[CE_DELAY];
12674 HandleElementChange(x, y, page);
12676 if (CAN_CHANGE(element))
12677 HandleElementChange(x, y, page);
12679 if (HAS_ACTION(element))
12680 ExecuteCustomElementAction(x, y, element, page);
12683 element = Feld[x][y];
12684 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12687 #if 0 // ---------------------------------------------------------------------
12689 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12693 element = Feld[x][y];
12694 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12696 if (IS_ANIMATED(graphic) &&
12697 !IS_MOVING(x, y) &&
12699 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12701 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12702 TEST_DrawTwinkleOnField(x, y);
12704 else if (IS_MOVING(x, y))
12705 ContinueMoving(x, y);
12712 case EL_EM_EXIT_OPEN:
12713 case EL_SP_EXIT_OPEN:
12714 case EL_STEEL_EXIT_OPEN:
12715 case EL_EM_STEEL_EXIT_OPEN:
12716 case EL_SP_TERMINAL:
12717 case EL_SP_TERMINAL_ACTIVE:
12718 case EL_EXTRA_TIME:
12719 case EL_SHIELD_NORMAL:
12720 case EL_SHIELD_DEADLY:
12721 if (IS_ANIMATED(graphic))
12722 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12725 case EL_DYNAMITE_ACTIVE:
12726 case EL_EM_DYNAMITE_ACTIVE:
12727 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12728 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12729 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12730 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12731 case EL_SP_DISK_RED_ACTIVE:
12732 CheckDynamite(x, y);
12735 case EL_AMOEBA_GROWING:
12736 AmoebeWaechst(x, y);
12739 case EL_AMOEBA_SHRINKING:
12740 AmoebaDisappearing(x, y);
12743 #if !USE_NEW_AMOEBA_CODE
12744 case EL_AMOEBA_WET:
12745 case EL_AMOEBA_DRY:
12746 case EL_AMOEBA_FULL:
12748 case EL_EMC_DRIPPER:
12749 AmoebeAbleger(x, y);
12753 case EL_GAME_OF_LIFE:
12758 case EL_EXIT_CLOSED:
12762 case EL_EM_EXIT_CLOSED:
12766 case EL_STEEL_EXIT_CLOSED:
12767 CheckExitSteel(x, y);
12770 case EL_EM_STEEL_EXIT_CLOSED:
12771 CheckExitSteelEM(x, y);
12774 case EL_SP_EXIT_CLOSED:
12778 case EL_EXPANDABLE_WALL_GROWING:
12779 case EL_EXPANDABLE_STEELWALL_GROWING:
12780 MauerWaechst(x, y);
12783 case EL_EXPANDABLE_WALL:
12784 case EL_EXPANDABLE_WALL_HORIZONTAL:
12785 case EL_EXPANDABLE_WALL_VERTICAL:
12786 case EL_EXPANDABLE_WALL_ANY:
12787 case EL_BD_EXPANDABLE_WALL:
12788 MauerAbleger(x, y);
12791 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12792 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12793 case EL_EXPANDABLE_STEELWALL_ANY:
12794 MauerAblegerStahl(x, y);
12798 CheckForDragon(x, y);
12804 case EL_ELEMENT_SNAPPING:
12805 case EL_DIAGONAL_SHRINKING:
12806 case EL_DIAGONAL_GROWING:
12809 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12811 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12816 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12817 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12822 #else // ---------------------------------------------------------------------
12824 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12828 element = Feld[x][y];
12829 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12831 if (IS_ANIMATED(graphic) &&
12832 !IS_MOVING(x, y) &&
12834 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12836 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12837 TEST_DrawTwinkleOnField(x, y);
12839 else if ((element == EL_ACID ||
12840 element == EL_EXIT_OPEN ||
12841 element == EL_EM_EXIT_OPEN ||
12842 element == EL_SP_EXIT_OPEN ||
12843 element == EL_STEEL_EXIT_OPEN ||
12844 element == EL_EM_STEEL_EXIT_OPEN ||
12845 element == EL_SP_TERMINAL ||
12846 element == EL_SP_TERMINAL_ACTIVE ||
12847 element == EL_EXTRA_TIME ||
12848 element == EL_SHIELD_NORMAL ||
12849 element == EL_SHIELD_DEADLY) &&
12850 IS_ANIMATED(graphic))
12851 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12852 else if (IS_MOVING(x, y))
12853 ContinueMoving(x, y);
12854 else if (IS_ACTIVE_BOMB(element))
12855 CheckDynamite(x, y);
12856 else if (element == EL_AMOEBA_GROWING)
12857 AmoebeWaechst(x, y);
12858 else if (element == EL_AMOEBA_SHRINKING)
12859 AmoebaDisappearing(x, y);
12861 #if !USE_NEW_AMOEBA_CODE
12862 else if (IS_AMOEBALIVE(element))
12863 AmoebeAbleger(x, y);
12866 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12868 else if (element == EL_EXIT_CLOSED)
12870 else if (element == EL_EM_EXIT_CLOSED)
12872 else if (element == EL_STEEL_EXIT_CLOSED)
12873 CheckExitSteel(x, y);
12874 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12875 CheckExitSteelEM(x, y);
12876 else if (element == EL_SP_EXIT_CLOSED)
12878 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12879 element == EL_EXPANDABLE_STEELWALL_GROWING)
12880 MauerWaechst(x, y);
12881 else if (element == EL_EXPANDABLE_WALL ||
12882 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12883 element == EL_EXPANDABLE_WALL_VERTICAL ||
12884 element == EL_EXPANDABLE_WALL_ANY ||
12885 element == EL_BD_EXPANDABLE_WALL)
12886 MauerAbleger(x, y);
12887 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12888 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12889 element == EL_EXPANDABLE_STEELWALL_ANY)
12890 MauerAblegerStahl(x, y);
12891 else if (element == EL_FLAMES)
12892 CheckForDragon(x, y);
12893 else if (element == EL_EXPLOSION)
12894 ; /* drawing of correct explosion animation is handled separately */
12895 else if (element == EL_ELEMENT_SNAPPING ||
12896 element == EL_DIAGONAL_SHRINKING ||
12897 element == EL_DIAGONAL_GROWING)
12899 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12901 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12903 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12904 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12906 #endif // ---------------------------------------------------------------------
12908 if (IS_BELT_ACTIVE(element))
12909 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12911 if (game.magic_wall_active)
12913 int jx = local_player->jx, jy = local_player->jy;
12915 /* play the element sound at the position nearest to the player */
12916 if ((element == EL_MAGIC_WALL_FULL ||
12917 element == EL_MAGIC_WALL_ACTIVE ||
12918 element == EL_MAGIC_WALL_EMPTYING ||
12919 element == EL_BD_MAGIC_WALL_FULL ||
12920 element == EL_BD_MAGIC_WALL_ACTIVE ||
12921 element == EL_BD_MAGIC_WALL_EMPTYING ||
12922 element == EL_DC_MAGIC_WALL_FULL ||
12923 element == EL_DC_MAGIC_WALL_ACTIVE ||
12924 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12925 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12934 debug_print_timestamp(0, "- time for MAIN loop: -->");
12937 #if USE_NEW_AMOEBA_CODE
12938 /* new experimental amoeba growth stuff */
12939 if (!(FrameCounter % 8))
12941 static unsigned long random = 1684108901;
12943 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12945 x = RND(lev_fieldx);
12946 y = RND(lev_fieldy);
12947 element = Feld[x][y];
12949 if (!IS_PLAYER(x,y) &&
12950 (element == EL_EMPTY ||
12951 CAN_GROW_INTO(element) ||
12952 element == EL_QUICKSAND_EMPTY ||
12953 element == EL_QUICKSAND_FAST_EMPTY ||
12954 element == EL_ACID_SPLASH_LEFT ||
12955 element == EL_ACID_SPLASH_RIGHT))
12957 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12958 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12959 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12960 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12961 Feld[x][y] = EL_AMOEBA_DROP;
12964 random = random * 129 + 1;
12970 if (game.explosions_delayed)
12973 game.explosions_delayed = FALSE;
12975 SCAN_PLAYFIELD(x, y)
12977 element = Feld[x][y];
12979 if (ExplodeField[x][y])
12980 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12981 else if (element == EL_EXPLOSION)
12982 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12984 ExplodeField[x][y] = EX_TYPE_NONE;
12987 game.explosions_delayed = TRUE;
12990 if (game.magic_wall_active)
12992 if (!(game.magic_wall_time_left % 4))
12994 int element = Feld[magic_wall_x][magic_wall_y];
12996 if (element == EL_BD_MAGIC_WALL_FULL ||
12997 element == EL_BD_MAGIC_WALL_ACTIVE ||
12998 element == EL_BD_MAGIC_WALL_EMPTYING)
12999 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13000 else if (element == EL_DC_MAGIC_WALL_FULL ||
13001 element == EL_DC_MAGIC_WALL_ACTIVE ||
13002 element == EL_DC_MAGIC_WALL_EMPTYING)
13003 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13005 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13008 if (game.magic_wall_time_left > 0)
13010 game.magic_wall_time_left--;
13012 if (!game.magic_wall_time_left)
13014 SCAN_PLAYFIELD(x, y)
13016 element = Feld[x][y];
13018 if (element == EL_MAGIC_WALL_ACTIVE ||
13019 element == EL_MAGIC_WALL_FULL)
13021 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13022 TEST_DrawLevelField(x, y);
13024 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13025 element == EL_BD_MAGIC_WALL_FULL)
13027 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13028 TEST_DrawLevelField(x, y);
13030 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13031 element == EL_DC_MAGIC_WALL_FULL)
13033 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13034 TEST_DrawLevelField(x, y);
13038 game.magic_wall_active = FALSE;
13043 if (game.light_time_left > 0)
13045 game.light_time_left--;
13047 if (game.light_time_left == 0)
13048 RedrawAllLightSwitchesAndInvisibleElements();
13051 if (game.timegate_time_left > 0)
13053 game.timegate_time_left--;
13055 if (game.timegate_time_left == 0)
13056 CloseAllOpenTimegates();
13059 if (game.lenses_time_left > 0)
13061 game.lenses_time_left--;
13063 if (game.lenses_time_left == 0)
13064 RedrawAllInvisibleElementsForLenses();
13067 if (game.magnify_time_left > 0)
13069 game.magnify_time_left--;
13071 if (game.magnify_time_left == 0)
13072 RedrawAllInvisibleElementsForMagnifier();
13075 for (i = 0; i < MAX_PLAYERS; i++)
13077 struct PlayerInfo *player = &stored_player[i];
13079 if (SHIELD_ON(player))
13081 if (player->shield_deadly_time_left)
13082 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13083 else if (player->shield_normal_time_left)
13084 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13088 #if USE_DELAYED_GFX_REDRAW
13089 SCAN_PLAYFIELD(x, y)
13092 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13094 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13095 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13098 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13099 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13101 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13102 DrawLevelField(x, y);
13104 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13105 DrawLevelFieldCrumbledSand(x, y);
13107 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13108 DrawLevelFieldCrumbledSandNeighbours(x, y);
13110 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13111 DrawTwinkleOnField(x, y);
13114 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13121 PlayAllPlayersSound();
13123 if (options.debug) /* calculate frames per second */
13125 static unsigned long fps_counter = 0;
13126 static int fps_frames = 0;
13127 unsigned long fps_delay_ms = Counter() - fps_counter;
13131 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13133 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13136 fps_counter = Counter();
13139 redraw_mask |= REDRAW_FPS;
13142 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13144 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13146 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13148 local_player->show_envelope = 0;
13152 debug_print_timestamp(0, "stop main loop profiling ");
13153 printf("----------------------------------------------------------\n");
13156 /* use random number generator in every frame to make it less predictable */
13157 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13161 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13163 int min_x = x, min_y = y, max_x = x, max_y = y;
13166 for (i = 0; i < MAX_PLAYERS; i++)
13168 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13170 if (!stored_player[i].active || &stored_player[i] == player)
13173 min_x = MIN(min_x, jx);
13174 min_y = MIN(min_y, jy);
13175 max_x = MAX(max_x, jx);
13176 max_y = MAX(max_y, jy);
13179 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13182 static boolean AllPlayersInVisibleScreen()
13186 for (i = 0; i < MAX_PLAYERS; i++)
13188 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13190 if (!stored_player[i].active)
13193 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13200 void ScrollLevel(int dx, int dy)
13203 /* (directly solved in BlitBitmap() now) */
13204 static Bitmap *bitmap_db_field2 = NULL;
13205 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13212 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13213 /* only horizontal XOR vertical scroll direction allowed */
13214 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13219 /* (directly solved in BlitBitmap() now) */
13220 if (bitmap_db_field2 == NULL)
13221 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13223 /* needed when blitting directly to same bitmap -- should not be needed with
13224 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13225 BlitBitmap(drawto_field, bitmap_db_field2,
13226 FX + TILEX * (dx == -1) - softscroll_offset,
13227 FY + TILEY * (dy == -1) - softscroll_offset,
13228 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13229 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13230 FX + TILEX * (dx == 1) - softscroll_offset,
13231 FY + TILEY * (dy == 1) - softscroll_offset);
13232 BlitBitmap(bitmap_db_field2, drawto_field,
13233 FX + TILEX * (dx == 1) - softscroll_offset,
13234 FY + TILEY * (dy == 1) - softscroll_offset,
13235 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13236 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13237 FX + TILEX * (dx == 1) - softscroll_offset,
13238 FY + TILEY * (dy == 1) - softscroll_offset);
13243 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13244 int xsize = (BX2 - BX1 + 1);
13245 int ysize = (BY2 - BY1 + 1);
13246 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13247 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13248 int step = (start < end ? +1 : -1);
13250 for (i = start; i != end; i += step)
13252 BlitBitmap(drawto_field, drawto_field,
13253 FX + TILEX * (dx != 0 ? i + step : 0),
13254 FY + TILEY * (dy != 0 ? i + step : 0),
13255 TILEX * (dx != 0 ? 1 : xsize),
13256 TILEY * (dy != 0 ? 1 : ysize),
13257 FX + TILEX * (dx != 0 ? i : 0),
13258 FY + TILEY * (dy != 0 ? i : 0));
13263 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13265 BlitBitmap(drawto_field, drawto_field,
13266 FX + TILEX * (dx == -1) - softscroll_offset,
13267 FY + TILEY * (dy == -1) - softscroll_offset,
13268 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13269 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13270 FX + TILEX * (dx == 1) - softscroll_offset,
13271 FY + TILEY * (dy == 1) - softscroll_offset);
13277 x = (dx == 1 ? BX1 : BX2);
13278 for (y = BY1; y <= BY2; y++)
13279 DrawScreenField(x, y);
13284 y = (dy == 1 ? BY1 : BY2);
13285 for (x = BX1; x <= BX2; x++)
13286 DrawScreenField(x, y);
13289 redraw_mask |= REDRAW_FIELD;
13292 static boolean canFallDown(struct PlayerInfo *player)
13294 int jx = player->jx, jy = player->jy;
13296 return (IN_LEV_FIELD(jx, jy + 1) &&
13297 (IS_FREE(jx, jy + 1) ||
13298 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13299 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13300 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13303 static boolean canPassField(int x, int y, int move_dir)
13305 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13306 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13307 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13308 int nextx = x + dx;
13309 int nexty = y + dy;
13310 int element = Feld[x][y];
13312 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13313 !CAN_MOVE(element) &&
13314 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13315 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13316 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13319 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13321 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13322 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13323 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13327 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13328 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13329 (IS_DIGGABLE(Feld[newx][newy]) ||
13330 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13331 canPassField(newx, newy, move_dir)));
13334 static void CheckGravityMovement(struct PlayerInfo *player)
13336 #if USE_PLAYER_GRAVITY
13337 if (player->gravity && !player->programmed_action)
13339 if (game.gravity && !player->programmed_action)
13342 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13343 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13344 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13345 int jx = player->jx, jy = player->jy;
13346 boolean player_is_moving_to_valid_field =
13347 (!player_is_snapping &&
13348 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13349 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13350 boolean player_can_fall_down = canFallDown(player);
13352 if (player_can_fall_down &&
13353 !player_is_moving_to_valid_field)
13354 player->programmed_action = MV_DOWN;
13358 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13360 return CheckGravityMovement(player);
13362 #if USE_PLAYER_GRAVITY
13363 if (player->gravity && !player->programmed_action)
13365 if (game.gravity && !player->programmed_action)
13368 int jx = player->jx, jy = player->jy;
13369 boolean field_under_player_is_free =
13370 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13371 boolean player_is_standing_on_valid_field =
13372 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13373 (IS_WALKABLE(Feld[jx][jy]) &&
13374 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13376 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13377 player->programmed_action = MV_DOWN;
13382 MovePlayerOneStep()
13383 -----------------------------------------------------------------------------
13384 dx, dy: direction (non-diagonal) to try to move the player to
13385 real_dx, real_dy: direction as read from input device (can be diagonal)
13388 boolean MovePlayerOneStep(struct PlayerInfo *player,
13389 int dx, int dy, int real_dx, int real_dy)
13391 int jx = player->jx, jy = player->jy;
13392 int new_jx = jx + dx, new_jy = jy + dy;
13393 #if !USE_FIXED_DONT_RUN_INTO
13397 boolean player_can_move = !player->cannot_move;
13399 if (!player->active || (!dx && !dy))
13400 return MP_NO_ACTION;
13402 player->MovDir = (dx < 0 ? MV_LEFT :
13403 dx > 0 ? MV_RIGHT :
13405 dy > 0 ? MV_DOWN : MV_NONE);
13407 if (!IN_LEV_FIELD(new_jx, new_jy))
13408 return MP_NO_ACTION;
13410 if (!player_can_move)
13412 if (player->MovPos == 0)
13414 player->is_moving = FALSE;
13415 player->is_digging = FALSE;
13416 player->is_collecting = FALSE;
13417 player->is_snapping = FALSE;
13418 player->is_pushing = FALSE;
13423 if (!options.network && game.centered_player_nr == -1 &&
13424 !AllPlayersInSight(player, new_jx, new_jy))
13425 return MP_NO_ACTION;
13427 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13428 return MP_NO_ACTION;
13431 #if !USE_FIXED_DONT_RUN_INTO
13432 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13434 /* (moved to DigField()) */
13435 if (player_can_move && DONT_RUN_INTO(element))
13437 if (element == EL_ACID && dx == 0 && dy == 1)
13439 SplashAcid(new_jx, new_jy);
13440 Feld[jx][jy] = EL_PLAYER_1;
13441 InitMovingField(jx, jy, MV_DOWN);
13442 Store[jx][jy] = EL_ACID;
13443 ContinueMoving(jx, jy);
13444 BuryPlayer(player);
13447 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13453 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13454 if (can_move != MP_MOVING)
13457 /* check if DigField() has caused relocation of the player */
13458 if (player->jx != jx || player->jy != jy)
13459 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13461 StorePlayer[jx][jy] = 0;
13462 player->last_jx = jx;
13463 player->last_jy = jy;
13464 player->jx = new_jx;
13465 player->jy = new_jy;
13466 StorePlayer[new_jx][new_jy] = player->element_nr;
13468 if (player->move_delay_value_next != -1)
13470 player->move_delay_value = player->move_delay_value_next;
13471 player->move_delay_value_next = -1;
13475 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13477 player->step_counter++;
13479 PlayerVisit[jx][jy] = FrameCounter;
13481 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13482 player->is_moving = TRUE;
13486 /* should better be called in MovePlayer(), but this breaks some tapes */
13487 ScrollPlayer(player, SCROLL_INIT);
13493 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13495 int jx = player->jx, jy = player->jy;
13496 int old_jx = jx, old_jy = jy;
13497 int moved = MP_NO_ACTION;
13499 if (!player->active)
13504 if (player->MovPos == 0)
13506 player->is_moving = FALSE;
13507 player->is_digging = FALSE;
13508 player->is_collecting = FALSE;
13509 player->is_snapping = FALSE;
13510 player->is_pushing = FALSE;
13516 if (player->move_delay > 0)
13519 player->move_delay = -1; /* set to "uninitialized" value */
13521 /* store if player is automatically moved to next field */
13522 player->is_auto_moving = (player->programmed_action != MV_NONE);
13524 /* remove the last programmed player action */
13525 player->programmed_action = 0;
13527 if (player->MovPos)
13529 /* should only happen if pre-1.2 tape recordings are played */
13530 /* this is only for backward compatibility */
13532 int original_move_delay_value = player->move_delay_value;
13535 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13539 /* scroll remaining steps with finest movement resolution */
13540 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13542 while (player->MovPos)
13544 ScrollPlayer(player, SCROLL_GO_ON);
13545 ScrollScreen(NULL, SCROLL_GO_ON);
13547 AdvanceFrameAndPlayerCounters(player->index_nr);
13553 player->move_delay_value = original_move_delay_value;
13556 player->is_active = FALSE;
13558 if (player->last_move_dir & MV_HORIZONTAL)
13560 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13561 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13565 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13566 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13569 #if USE_FIXED_BORDER_RUNNING_GFX
13570 if (!moved && !player->is_active)
13572 player->is_moving = FALSE;
13573 player->is_digging = FALSE;
13574 player->is_collecting = FALSE;
13575 player->is_snapping = FALSE;
13576 player->is_pushing = FALSE;
13584 if (moved & MP_MOVING && !ScreenMovPos &&
13585 (player->index_nr == game.centered_player_nr ||
13586 game.centered_player_nr == -1))
13588 if (moved & MP_MOVING && !ScreenMovPos &&
13589 (player == local_player || !options.network))
13592 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13593 int offset = game.scroll_delay_value;
13595 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13597 /* actual player has left the screen -- scroll in that direction */
13598 if (jx != old_jx) /* player has moved horizontally */
13599 scroll_x += (jx - old_jx);
13600 else /* player has moved vertically */
13601 scroll_y += (jy - old_jy);
13605 if (jx != old_jx) /* player has moved horizontally */
13607 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13608 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13609 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13611 /* don't scroll over playfield boundaries */
13612 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13613 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13615 /* don't scroll more than one field at a time */
13616 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13618 /* don't scroll against the player's moving direction */
13619 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13620 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13621 scroll_x = old_scroll_x;
13623 else /* player has moved vertically */
13625 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13626 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13627 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13629 /* don't scroll over playfield boundaries */
13630 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13631 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13633 /* don't scroll more than one field at a time */
13634 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13636 /* don't scroll against the player's moving direction */
13637 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13638 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13639 scroll_y = old_scroll_y;
13643 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13646 if (!options.network && game.centered_player_nr == -1 &&
13647 !AllPlayersInVisibleScreen())
13649 scroll_x = old_scroll_x;
13650 scroll_y = old_scroll_y;
13654 if (!options.network && !AllPlayersInVisibleScreen())
13656 scroll_x = old_scroll_x;
13657 scroll_y = old_scroll_y;
13662 ScrollScreen(player, SCROLL_INIT);
13663 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13668 player->StepFrame = 0;
13670 if (moved & MP_MOVING)
13672 if (old_jx != jx && old_jy == jy)
13673 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13674 else if (old_jx == jx && old_jy != jy)
13675 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13677 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13679 player->last_move_dir = player->MovDir;
13680 player->is_moving = TRUE;
13681 player->is_snapping = FALSE;
13682 player->is_switching = FALSE;
13683 player->is_dropping = FALSE;
13684 player->is_dropping_pressed = FALSE;
13685 player->drop_pressed_delay = 0;
13688 /* should better be called here than above, but this breaks some tapes */
13689 ScrollPlayer(player, SCROLL_INIT);
13694 CheckGravityMovementWhenNotMoving(player);
13696 player->is_moving = FALSE;
13698 /* at this point, the player is allowed to move, but cannot move right now
13699 (e.g. because of something blocking the way) -- ensure that the player
13700 is also allowed to move in the next frame (in old versions before 3.1.1,
13701 the player was forced to wait again for eight frames before next try) */
13703 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13704 player->move_delay = 0; /* allow direct movement in the next frame */
13707 if (player->move_delay == -1) /* not yet initialized by DigField() */
13708 player->move_delay = player->move_delay_value;
13710 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13712 TestIfPlayerTouchesBadThing(jx, jy);
13713 TestIfPlayerTouchesCustomElement(jx, jy);
13716 if (!player->active)
13717 RemovePlayer(player);
13722 void ScrollPlayer(struct PlayerInfo *player, int mode)
13724 int jx = player->jx, jy = player->jy;
13725 int last_jx = player->last_jx, last_jy = player->last_jy;
13726 int move_stepsize = TILEX / player->move_delay_value;
13728 #if USE_NEW_PLAYER_SPEED
13729 if (!player->active)
13732 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13735 if (!player->active || player->MovPos == 0)
13739 if (mode == SCROLL_INIT)
13741 player->actual_frame_counter = FrameCounter;
13742 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13744 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13745 Feld[last_jx][last_jy] == EL_EMPTY)
13747 int last_field_block_delay = 0; /* start with no blocking at all */
13748 int block_delay_adjustment = player->block_delay_adjustment;
13750 /* if player blocks last field, add delay for exactly one move */
13751 if (player->block_last_field)
13753 last_field_block_delay += player->move_delay_value;
13755 /* when blocking enabled, prevent moving up despite gravity */
13756 #if USE_PLAYER_GRAVITY
13757 if (player->gravity && player->MovDir == MV_UP)
13758 block_delay_adjustment = -1;
13760 if (game.gravity && player->MovDir == MV_UP)
13761 block_delay_adjustment = -1;
13765 /* add block delay adjustment (also possible when not blocking) */
13766 last_field_block_delay += block_delay_adjustment;
13768 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13769 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13772 #if USE_NEW_PLAYER_SPEED
13773 if (player->MovPos != 0) /* player has not yet reached destination */
13779 else if (!FrameReached(&player->actual_frame_counter, 1))
13782 #if USE_NEW_PLAYER_SPEED
13783 if (player->MovPos != 0)
13785 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13786 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13788 /* before DrawPlayer() to draw correct player graphic for this case */
13789 if (player->MovPos == 0)
13790 CheckGravityMovement(player);
13793 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13794 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13796 /* before DrawPlayer() to draw correct player graphic for this case */
13797 if (player->MovPos == 0)
13798 CheckGravityMovement(player);
13801 if (player->MovPos == 0) /* player reached destination field */
13803 if (player->move_delay_reset_counter > 0)
13805 player->move_delay_reset_counter--;
13807 if (player->move_delay_reset_counter == 0)
13809 /* continue with normal speed after quickly moving through gate */
13810 HALVE_PLAYER_SPEED(player);
13812 /* be able to make the next move without delay */
13813 player->move_delay = 0;
13817 player->last_jx = jx;
13818 player->last_jy = jy;
13820 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13821 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13823 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13825 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13826 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13828 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13830 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13831 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13833 DrawPlayer(player); /* needed here only to cleanup last field */
13834 RemovePlayer(player);
13836 if (local_player->friends_still_needed == 0 ||
13837 IS_SP_ELEMENT(Feld[jx][jy]))
13838 PlayerWins(player);
13841 /* this breaks one level: "machine", level 000 */
13843 int move_direction = player->MovDir;
13844 int enter_side = MV_DIR_OPPOSITE(move_direction);
13845 int leave_side = move_direction;
13846 int old_jx = last_jx;
13847 int old_jy = last_jy;
13848 int old_element = Feld[old_jx][old_jy];
13849 int new_element = Feld[jx][jy];
13851 if (IS_CUSTOM_ELEMENT(old_element))
13852 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13854 player->index_bit, leave_side);
13856 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13857 CE_PLAYER_LEAVES_X,
13858 player->index_bit, leave_side);
13860 if (IS_CUSTOM_ELEMENT(new_element))
13861 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13862 player->index_bit, enter_side);
13864 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13865 CE_PLAYER_ENTERS_X,
13866 player->index_bit, enter_side);
13868 #if USE_FIX_CE_ACTION_WITH_PLAYER
13869 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13870 CE_MOVE_OF_X, move_direction);
13872 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13873 CE_MOVE_OF_X, move_direction);
13877 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13879 TestIfPlayerTouchesBadThing(jx, jy);
13880 TestIfPlayerTouchesCustomElement(jx, jy);
13882 /* needed because pushed element has not yet reached its destination,
13883 so it would trigger a change event at its previous field location */
13884 if (!player->is_pushing)
13885 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13887 if (!player->active)
13888 RemovePlayer(player);
13891 if (!local_player->LevelSolved && level.use_step_counter)
13901 if (TimeLeft <= 10 && setup.time_limit)
13902 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13905 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13907 DisplayGameControlValues();
13909 DrawGameValue_Time(TimeLeft);
13912 if (!TimeLeft && setup.time_limit)
13913 for (i = 0; i < MAX_PLAYERS; i++)
13914 KillPlayer(&stored_player[i]);
13917 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13919 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13921 DisplayGameControlValues();
13924 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13925 DrawGameValue_Time(TimePlayed);
13929 if (tape.single_step && tape.recording && !tape.pausing &&
13930 !player->programmed_action)
13931 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13935 void ScrollScreen(struct PlayerInfo *player, int mode)
13937 static unsigned long screen_frame_counter = 0;
13939 if (mode == SCROLL_INIT)
13941 /* set scrolling step size according to actual player's moving speed */
13942 ScrollStepSize = TILEX / player->move_delay_value;
13944 screen_frame_counter = FrameCounter;
13945 ScreenMovDir = player->MovDir;
13946 ScreenMovPos = player->MovPos;
13947 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13950 else if (!FrameReached(&screen_frame_counter, 1))
13955 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13956 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13957 redraw_mask |= REDRAW_FIELD;
13960 ScreenMovDir = MV_NONE;
13963 void TestIfPlayerTouchesCustomElement(int x, int y)
13965 static int xy[4][2] =
13972 static int trigger_sides[4][2] =
13974 /* center side border side */
13975 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13976 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13977 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13978 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13980 static int touch_dir[4] =
13982 MV_LEFT | MV_RIGHT,
13987 int center_element = Feld[x][y]; /* should always be non-moving! */
13990 for (i = 0; i < NUM_DIRECTIONS; i++)
13992 int xx = x + xy[i][0];
13993 int yy = y + xy[i][1];
13994 int center_side = trigger_sides[i][0];
13995 int border_side = trigger_sides[i][1];
13996 int border_element;
13998 if (!IN_LEV_FIELD(xx, yy))
14001 if (IS_PLAYER(x, y)) /* player found at center element */
14003 struct PlayerInfo *player = PLAYERINFO(x, y);
14005 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14006 border_element = Feld[xx][yy]; /* may be moving! */
14007 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14008 border_element = Feld[xx][yy];
14009 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14010 border_element = MovingOrBlocked2Element(xx, yy);
14012 continue; /* center and border element do not touch */
14014 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14015 player->index_bit, border_side);
14016 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14017 CE_PLAYER_TOUCHES_X,
14018 player->index_bit, border_side);
14020 #if USE_FIX_CE_ACTION_WITH_PLAYER
14022 /* use player element that is initially defined in the level playfield,
14023 not the player element that corresponds to the runtime player number
14024 (example: a level that contains EL_PLAYER_3 as the only player would
14025 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14026 int player_element = PLAYERINFO(x, y)->initial_element;
14028 CheckElementChangeBySide(xx, yy, border_element, player_element,
14029 CE_TOUCHING_X, border_side);
14033 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14035 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14037 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14039 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14040 continue; /* center and border element do not touch */
14043 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14044 player->index_bit, center_side);
14045 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14046 CE_PLAYER_TOUCHES_X,
14047 player->index_bit, center_side);
14049 #if USE_FIX_CE_ACTION_WITH_PLAYER
14051 /* use player element that is initially defined in the level playfield,
14052 not the player element that corresponds to the runtime player number
14053 (example: a level that contains EL_PLAYER_3 as the only player would
14054 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14055 int player_element = PLAYERINFO(xx, yy)->initial_element;
14057 CheckElementChangeBySide(x, y, center_element, player_element,
14058 CE_TOUCHING_X, center_side);
14067 #if USE_ELEMENT_TOUCHING_BUGFIX
14069 void TestIfElementTouchesCustomElement(int x, int y)
14071 static int xy[4][2] =
14078 static int trigger_sides[4][2] =
14080 /* center side border side */
14081 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14082 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14083 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14084 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14086 static int touch_dir[4] =
14088 MV_LEFT | MV_RIGHT,
14093 boolean change_center_element = FALSE;
14094 int center_element = Feld[x][y]; /* should always be non-moving! */
14095 int border_element_old[NUM_DIRECTIONS];
14098 for (i = 0; i < NUM_DIRECTIONS; i++)
14100 int xx = x + xy[i][0];
14101 int yy = y + xy[i][1];
14102 int border_element;
14104 border_element_old[i] = -1;
14106 if (!IN_LEV_FIELD(xx, yy))
14109 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14110 border_element = Feld[xx][yy]; /* may be moving! */
14111 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14112 border_element = Feld[xx][yy];
14113 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14114 border_element = MovingOrBlocked2Element(xx, yy);
14116 continue; /* center and border element do not touch */
14118 border_element_old[i] = border_element;
14121 for (i = 0; i < NUM_DIRECTIONS; i++)
14123 int xx = x + xy[i][0];
14124 int yy = y + xy[i][1];
14125 int center_side = trigger_sides[i][0];
14126 int border_element = border_element_old[i];
14128 if (border_element == -1)
14131 /* check for change of border element */
14132 CheckElementChangeBySide(xx, yy, border_element, center_element,
14133 CE_TOUCHING_X, center_side);
14135 /* (center element cannot be player, so we dont have to check this here) */
14138 for (i = 0; i < NUM_DIRECTIONS; i++)
14140 int xx = x + xy[i][0];
14141 int yy = y + xy[i][1];
14142 int border_side = trigger_sides[i][1];
14143 int border_element = border_element_old[i];
14145 if (border_element == -1)
14148 /* check for change of center element (but change it only once) */
14149 if (!change_center_element)
14150 change_center_element =
14151 CheckElementChangeBySide(x, y, center_element, border_element,
14152 CE_TOUCHING_X, border_side);
14154 #if USE_FIX_CE_ACTION_WITH_PLAYER
14155 if (IS_PLAYER(xx, yy))
14157 /* use player element that is initially defined in the level playfield,
14158 not the player element that corresponds to the runtime player number
14159 (example: a level that contains EL_PLAYER_3 as the only player would
14160 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14161 int player_element = PLAYERINFO(xx, yy)->initial_element;
14163 CheckElementChangeBySide(x, y, center_element, player_element,
14164 CE_TOUCHING_X, border_side);
14172 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14174 static int xy[4][2] =
14181 static int trigger_sides[4][2] =
14183 /* center side border side */
14184 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14185 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14186 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14187 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14189 static int touch_dir[4] =
14191 MV_LEFT | MV_RIGHT,
14196 boolean change_center_element = FALSE;
14197 int center_element = Feld[x][y]; /* should always be non-moving! */
14200 for (i = 0; i < NUM_DIRECTIONS; i++)
14202 int xx = x + xy[i][0];
14203 int yy = y + xy[i][1];
14204 int center_side = trigger_sides[i][0];
14205 int border_side = trigger_sides[i][1];
14206 int border_element;
14208 if (!IN_LEV_FIELD(xx, yy))
14211 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14212 border_element = Feld[xx][yy]; /* may be moving! */
14213 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14214 border_element = Feld[xx][yy];
14215 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14216 border_element = MovingOrBlocked2Element(xx, yy);
14218 continue; /* center and border element do not touch */
14220 /* check for change of center element (but change it only once) */
14221 if (!change_center_element)
14222 change_center_element =
14223 CheckElementChangeBySide(x, y, center_element, border_element,
14224 CE_TOUCHING_X, border_side);
14226 /* check for change of border element */
14227 CheckElementChangeBySide(xx, yy, border_element, center_element,
14228 CE_TOUCHING_X, center_side);
14234 void TestIfElementHitsCustomElement(int x, int y, int direction)
14236 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14237 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14238 int hitx = x + dx, hity = y + dy;
14239 int hitting_element = Feld[x][y];
14240 int touched_element;
14242 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14245 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14246 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14248 if (IN_LEV_FIELD(hitx, hity))
14250 int opposite_direction = MV_DIR_OPPOSITE(direction);
14251 int hitting_side = direction;
14252 int touched_side = opposite_direction;
14253 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14254 MovDir[hitx][hity] != direction ||
14255 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14261 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14262 CE_HITTING_X, touched_side);
14264 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14265 CE_HIT_BY_X, hitting_side);
14267 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14268 CE_HIT_BY_SOMETHING, opposite_direction);
14270 #if USE_FIX_CE_ACTION_WITH_PLAYER
14271 if (IS_PLAYER(hitx, hity))
14273 /* use player element that is initially defined in the level playfield,
14274 not the player element that corresponds to the runtime player number
14275 (example: a level that contains EL_PLAYER_3 as the only player would
14276 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14277 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14279 CheckElementChangeBySide(x, y, hitting_element, player_element,
14280 CE_HITTING_X, touched_side);
14286 /* "hitting something" is also true when hitting the playfield border */
14287 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14288 CE_HITTING_SOMETHING, direction);
14292 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14294 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14295 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14296 int hitx = x + dx, hity = y + dy;
14297 int hitting_element = Feld[x][y];
14298 int touched_element;
14300 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14301 !IS_FREE(hitx, hity) &&
14302 (!IS_MOVING(hitx, hity) ||
14303 MovDir[hitx][hity] != direction ||
14304 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14307 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14311 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14315 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14316 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14318 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14319 EP_CAN_SMASH_EVERYTHING, direction);
14321 if (IN_LEV_FIELD(hitx, hity))
14323 int opposite_direction = MV_DIR_OPPOSITE(direction);
14324 int hitting_side = direction;
14325 int touched_side = opposite_direction;
14327 int touched_element = MovingOrBlocked2Element(hitx, hity);
14330 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14331 MovDir[hitx][hity] != direction ||
14332 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14341 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14342 CE_SMASHED_BY_SOMETHING, opposite_direction);
14344 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14345 CE_OTHER_IS_SMASHING, touched_side);
14347 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14348 CE_OTHER_GETS_SMASHED, hitting_side);
14354 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14356 int i, kill_x = -1, kill_y = -1;
14358 int bad_element = -1;
14359 static int test_xy[4][2] =
14366 static int test_dir[4] =
14374 for (i = 0; i < NUM_DIRECTIONS; i++)
14376 int test_x, test_y, test_move_dir, test_element;
14378 test_x = good_x + test_xy[i][0];
14379 test_y = good_y + test_xy[i][1];
14381 if (!IN_LEV_FIELD(test_x, test_y))
14385 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14387 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14389 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14390 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14392 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14393 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14397 bad_element = test_element;
14403 if (kill_x != -1 || kill_y != -1)
14405 if (IS_PLAYER(good_x, good_y))
14407 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14409 if (player->shield_deadly_time_left > 0 &&
14410 !IS_INDESTRUCTIBLE(bad_element))
14411 Bang(kill_x, kill_y);
14412 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14413 KillPlayer(player);
14416 Bang(good_x, good_y);
14420 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14422 int i, kill_x = -1, kill_y = -1;
14423 int bad_element = Feld[bad_x][bad_y];
14424 static int test_xy[4][2] =
14431 static int touch_dir[4] =
14433 MV_LEFT | MV_RIGHT,
14438 static int test_dir[4] =
14446 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14449 for (i = 0; i < NUM_DIRECTIONS; i++)
14451 int test_x, test_y, test_move_dir, test_element;
14453 test_x = bad_x + test_xy[i][0];
14454 test_y = bad_y + test_xy[i][1];
14456 if (!IN_LEV_FIELD(test_x, test_y))
14460 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14462 test_element = Feld[test_x][test_y];
14464 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14465 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14467 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14468 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14470 /* good thing is player or penguin that does not move away */
14471 if (IS_PLAYER(test_x, test_y))
14473 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14475 if (bad_element == EL_ROBOT && player->is_moving)
14476 continue; /* robot does not kill player if he is moving */
14478 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14480 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14481 continue; /* center and border element do not touch */
14489 else if (test_element == EL_PENGUIN)
14499 if (kill_x != -1 || kill_y != -1)
14501 if (IS_PLAYER(kill_x, kill_y))
14503 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14505 if (player->shield_deadly_time_left > 0 &&
14506 !IS_INDESTRUCTIBLE(bad_element))
14507 Bang(bad_x, bad_y);
14508 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14509 KillPlayer(player);
14512 Bang(kill_x, kill_y);
14516 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14518 int bad_element = Feld[bad_x][bad_y];
14519 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14520 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14521 int test_x = bad_x + dx, test_y = bad_y + dy;
14522 int test_move_dir, test_element;
14523 int kill_x = -1, kill_y = -1;
14525 if (!IN_LEV_FIELD(test_x, test_y))
14529 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14531 test_element = Feld[test_x][test_y];
14533 if (test_move_dir != bad_move_dir)
14535 /* good thing can be player or penguin that does not move away */
14536 if (IS_PLAYER(test_x, test_y))
14538 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14540 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14541 player as being hit when he is moving towards the bad thing, because
14542 the "get hit by" condition would be lost after the player stops) */
14543 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14544 return; /* player moves away from bad thing */
14549 else if (test_element == EL_PENGUIN)
14556 if (kill_x != -1 || kill_y != -1)
14558 if (IS_PLAYER(kill_x, kill_y))
14560 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14562 if (player->shield_deadly_time_left > 0 &&
14563 !IS_INDESTRUCTIBLE(bad_element))
14564 Bang(bad_x, bad_y);
14565 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14566 KillPlayer(player);
14569 Bang(kill_x, kill_y);
14573 void TestIfPlayerTouchesBadThing(int x, int y)
14575 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14578 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14580 TestIfGoodThingHitsBadThing(x, y, move_dir);
14583 void TestIfBadThingTouchesPlayer(int x, int y)
14585 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14588 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14590 TestIfBadThingHitsGoodThing(x, y, move_dir);
14593 void TestIfFriendTouchesBadThing(int x, int y)
14595 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14598 void TestIfBadThingTouchesFriend(int x, int y)
14600 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14603 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14605 int i, kill_x = bad_x, kill_y = bad_y;
14606 static int xy[4][2] =
14614 for (i = 0; i < NUM_DIRECTIONS; i++)
14618 x = bad_x + xy[i][0];
14619 y = bad_y + xy[i][1];
14620 if (!IN_LEV_FIELD(x, y))
14623 element = Feld[x][y];
14624 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14625 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14633 if (kill_x != bad_x || kill_y != bad_y)
14634 Bang(bad_x, bad_y);
14637 void KillPlayer(struct PlayerInfo *player)
14639 int jx = player->jx, jy = player->jy;
14641 if (!player->active)
14645 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14646 player->killed, player->active, player->reanimated);
14649 /* the following code was introduced to prevent an infinite loop when calling
14651 -> CheckTriggeredElementChangeExt()
14652 -> ExecuteCustomElementAction()
14654 -> (infinitely repeating the above sequence of function calls)
14655 which occurs when killing the player while having a CE with the setting
14656 "kill player X when explosion of <player X>"; the solution using a new
14657 field "player->killed" was chosen for backwards compatibility, although
14658 clever use of the fields "player->active" etc. would probably also work */
14660 if (player->killed)
14664 player->killed = TRUE;
14666 /* remove accessible field at the player's position */
14667 Feld[jx][jy] = EL_EMPTY;
14669 /* deactivate shield (else Bang()/Explode() would not work right) */
14670 player->shield_normal_time_left = 0;
14671 player->shield_deadly_time_left = 0;
14674 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14675 player->killed, player->active, player->reanimated);
14681 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14682 player->killed, player->active, player->reanimated);
14685 #if USE_PLAYER_REANIMATION
14687 if (player->reanimated) /* killed player may have been reanimated */
14688 player->killed = player->reanimated = FALSE;
14690 BuryPlayer(player);
14692 if (player->killed) /* player may have been reanimated */
14693 BuryPlayer(player);
14696 BuryPlayer(player);
14700 static void KillPlayerUnlessEnemyProtected(int x, int y)
14702 if (!PLAYER_ENEMY_PROTECTED(x, y))
14703 KillPlayer(PLAYERINFO(x, y));
14706 static void KillPlayerUnlessExplosionProtected(int x, int y)
14708 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14709 KillPlayer(PLAYERINFO(x, y));
14712 void BuryPlayer(struct PlayerInfo *player)
14714 int jx = player->jx, jy = player->jy;
14716 if (!player->active)
14719 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14720 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14722 player->GameOver = TRUE;
14723 RemovePlayer(player);
14726 void RemovePlayer(struct PlayerInfo *player)
14728 int jx = player->jx, jy = player->jy;
14729 int i, found = FALSE;
14731 player->present = FALSE;
14732 player->active = FALSE;
14734 if (!ExplodeField[jx][jy])
14735 StorePlayer[jx][jy] = 0;
14737 if (player->is_moving)
14738 TEST_DrawLevelField(player->last_jx, player->last_jy);
14740 for (i = 0; i < MAX_PLAYERS; i++)
14741 if (stored_player[i].active)
14745 AllPlayersGone = TRUE;
14751 #if USE_NEW_SNAP_DELAY
14752 static void setFieldForSnapping(int x, int y, int element, int direction)
14754 struct ElementInfo *ei = &element_info[element];
14755 int direction_bit = MV_DIR_TO_BIT(direction);
14756 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14757 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14758 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14760 Feld[x][y] = EL_ELEMENT_SNAPPING;
14761 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14763 ResetGfxAnimation(x, y);
14765 GfxElement[x][y] = element;
14766 GfxAction[x][y] = action;
14767 GfxDir[x][y] = direction;
14768 GfxFrame[x][y] = -1;
14773 =============================================================================
14774 checkDiagonalPushing()
14775 -----------------------------------------------------------------------------
14776 check if diagonal input device direction results in pushing of object
14777 (by checking if the alternative direction is walkable, diggable, ...)
14778 =============================================================================
14781 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14782 int x, int y, int real_dx, int real_dy)
14784 int jx, jy, dx, dy, xx, yy;
14786 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14789 /* diagonal direction: check alternative direction */
14794 xx = jx + (dx == 0 ? real_dx : 0);
14795 yy = jy + (dy == 0 ? real_dy : 0);
14797 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14801 =============================================================================
14803 -----------------------------------------------------------------------------
14804 x, y: field next to player (non-diagonal) to try to dig to
14805 real_dx, real_dy: direction as read from input device (can be diagonal)
14806 =============================================================================
14809 static int DigField(struct PlayerInfo *player,
14810 int oldx, int oldy, int x, int y,
14811 int real_dx, int real_dy, int mode)
14813 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14814 boolean player_was_pushing = player->is_pushing;
14815 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14816 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14817 int jx = oldx, jy = oldy;
14818 int dx = x - jx, dy = y - jy;
14819 int nextx = x + dx, nexty = y + dy;
14820 int move_direction = (dx == -1 ? MV_LEFT :
14821 dx == +1 ? MV_RIGHT :
14823 dy == +1 ? MV_DOWN : MV_NONE);
14824 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14825 int dig_side = MV_DIR_OPPOSITE(move_direction);
14826 int old_element = Feld[jx][jy];
14827 #if USE_FIXED_DONT_RUN_INTO
14828 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14834 if (is_player) /* function can also be called by EL_PENGUIN */
14836 if (player->MovPos == 0)
14838 player->is_digging = FALSE;
14839 player->is_collecting = FALSE;
14842 if (player->MovPos == 0) /* last pushing move finished */
14843 player->is_pushing = FALSE;
14845 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14847 player->is_switching = FALSE;
14848 player->push_delay = -1;
14850 return MP_NO_ACTION;
14854 #if !USE_FIXED_DONT_RUN_INTO
14855 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14856 return MP_NO_ACTION;
14859 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14860 old_element = Back[jx][jy];
14862 /* in case of element dropped at player position, check background */
14863 else if (Back[jx][jy] != EL_EMPTY &&
14864 game.engine_version >= VERSION_IDENT(2,2,0,0))
14865 old_element = Back[jx][jy];
14867 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14868 return MP_NO_ACTION; /* field has no opening in this direction */
14870 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14871 return MP_NO_ACTION; /* field has no opening in this direction */
14873 #if USE_FIXED_DONT_RUN_INTO
14874 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14878 Feld[jx][jy] = player->artwork_element;
14879 InitMovingField(jx, jy, MV_DOWN);
14880 Store[jx][jy] = EL_ACID;
14881 ContinueMoving(jx, jy);
14882 BuryPlayer(player);
14884 return MP_DONT_RUN_INTO;
14888 #if USE_FIXED_DONT_RUN_INTO
14889 if (player_can_move && DONT_RUN_INTO(element))
14891 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14893 return MP_DONT_RUN_INTO;
14897 #if USE_FIXED_DONT_RUN_INTO
14898 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14899 return MP_NO_ACTION;
14902 #if !USE_FIXED_DONT_RUN_INTO
14903 element = Feld[x][y];
14906 collect_count = element_info[element].collect_count_initial;
14908 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14909 return MP_NO_ACTION;
14911 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14912 player_can_move = player_can_move_or_snap;
14914 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14915 game.engine_version >= VERSION_IDENT(2,2,0,0))
14917 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14918 player->index_bit, dig_side);
14919 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14920 player->index_bit, dig_side);
14922 if (element == EL_DC_LANDMINE)
14925 if (Feld[x][y] != element) /* field changed by snapping */
14928 return MP_NO_ACTION;
14931 #if USE_PLAYER_GRAVITY
14932 if (player->gravity && is_player && !player->is_auto_moving &&
14933 canFallDown(player) && move_direction != MV_DOWN &&
14934 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14935 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14937 if (game.gravity && is_player && !player->is_auto_moving &&
14938 canFallDown(player) && move_direction != MV_DOWN &&
14939 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14940 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14943 if (player_can_move &&
14944 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14946 int sound_element = SND_ELEMENT(element);
14947 int sound_action = ACTION_WALKING;
14949 if (IS_RND_GATE(element))
14951 if (!player->key[RND_GATE_NR(element)])
14952 return MP_NO_ACTION;
14954 else if (IS_RND_GATE_GRAY(element))
14956 if (!player->key[RND_GATE_GRAY_NR(element)])
14957 return MP_NO_ACTION;
14959 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14961 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14962 return MP_NO_ACTION;
14964 else if (element == EL_EXIT_OPEN ||
14965 element == EL_EM_EXIT_OPEN ||
14967 element == EL_EM_EXIT_OPENING ||
14969 element == EL_STEEL_EXIT_OPEN ||
14970 element == EL_EM_STEEL_EXIT_OPEN ||
14972 element == EL_EM_STEEL_EXIT_OPENING ||
14974 element == EL_SP_EXIT_OPEN ||
14975 element == EL_SP_EXIT_OPENING)
14977 sound_action = ACTION_PASSING; /* player is passing exit */
14979 else if (element == EL_EMPTY)
14981 sound_action = ACTION_MOVING; /* nothing to walk on */
14984 /* play sound from background or player, whatever is available */
14985 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14986 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14988 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14990 else if (player_can_move &&
14991 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14993 if (!ACCESS_FROM(element, opposite_direction))
14994 return MP_NO_ACTION; /* field not accessible from this direction */
14996 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14997 return MP_NO_ACTION;
14999 if (IS_EM_GATE(element))
15001 if (!player->key[EM_GATE_NR(element)])
15002 return MP_NO_ACTION;
15004 else if (IS_EM_GATE_GRAY(element))
15006 if (!player->key[EM_GATE_GRAY_NR(element)])
15007 return MP_NO_ACTION;
15009 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15011 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15012 return MP_NO_ACTION;
15014 else if (IS_EMC_GATE(element))
15016 if (!player->key[EMC_GATE_NR(element)])
15017 return MP_NO_ACTION;
15019 else if (IS_EMC_GATE_GRAY(element))
15021 if (!player->key[EMC_GATE_GRAY_NR(element)])
15022 return MP_NO_ACTION;
15024 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15026 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15027 return MP_NO_ACTION;
15029 else if (element == EL_DC_GATE_WHITE ||
15030 element == EL_DC_GATE_WHITE_GRAY ||
15031 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15033 if (player->num_white_keys == 0)
15034 return MP_NO_ACTION;
15036 player->num_white_keys--;
15038 else if (IS_SP_PORT(element))
15040 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15041 element == EL_SP_GRAVITY_PORT_RIGHT ||
15042 element == EL_SP_GRAVITY_PORT_UP ||
15043 element == EL_SP_GRAVITY_PORT_DOWN)
15044 #if USE_PLAYER_GRAVITY
15045 player->gravity = !player->gravity;
15047 game.gravity = !game.gravity;
15049 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15050 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15051 element == EL_SP_GRAVITY_ON_PORT_UP ||
15052 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15053 #if USE_PLAYER_GRAVITY
15054 player->gravity = TRUE;
15056 game.gravity = TRUE;
15058 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15059 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15060 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15061 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15062 #if USE_PLAYER_GRAVITY
15063 player->gravity = FALSE;
15065 game.gravity = FALSE;
15069 /* automatically move to the next field with double speed */
15070 player->programmed_action = move_direction;
15072 if (player->move_delay_reset_counter == 0)
15074 player->move_delay_reset_counter = 2; /* two double speed steps */
15076 DOUBLE_PLAYER_SPEED(player);
15079 PlayLevelSoundAction(x, y, ACTION_PASSING);
15081 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15085 if (mode != DF_SNAP)
15087 GfxElement[x][y] = GFX_ELEMENT(element);
15088 player->is_digging = TRUE;
15091 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15093 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15094 player->index_bit, dig_side);
15096 if (mode == DF_SNAP)
15098 #if USE_NEW_SNAP_DELAY
15099 if (level.block_snap_field)
15100 setFieldForSnapping(x, y, element, move_direction);
15102 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15104 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15107 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15108 player->index_bit, dig_side);
15111 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15115 if (is_player && mode != DF_SNAP)
15117 GfxElement[x][y] = element;
15118 player->is_collecting = TRUE;
15121 if (element == EL_SPEED_PILL)
15123 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15125 else if (element == EL_EXTRA_TIME && level.time > 0)
15127 TimeLeft += level.extra_time;
15130 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15132 DisplayGameControlValues();
15134 DrawGameValue_Time(TimeLeft);
15137 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15139 player->shield_normal_time_left += level.shield_normal_time;
15140 if (element == EL_SHIELD_DEADLY)
15141 player->shield_deadly_time_left += level.shield_deadly_time;
15143 else if (element == EL_DYNAMITE ||
15144 element == EL_EM_DYNAMITE ||
15145 element == EL_SP_DISK_RED)
15147 if (player->inventory_size < MAX_INVENTORY_SIZE)
15148 player->inventory_element[player->inventory_size++] = element;
15150 DrawGameDoorValues();
15152 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15154 player->dynabomb_count++;
15155 player->dynabombs_left++;
15157 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15159 player->dynabomb_size++;
15161 else if (element == EL_DYNABOMB_INCREASE_POWER)
15163 player->dynabomb_xl = TRUE;
15165 else if (IS_KEY(element))
15167 player->key[KEY_NR(element)] = TRUE;
15169 DrawGameDoorValues();
15171 else if (element == EL_DC_KEY_WHITE)
15173 player->num_white_keys++;
15175 /* display white keys? */
15176 /* DrawGameDoorValues(); */
15178 else if (IS_ENVELOPE(element))
15180 player->show_envelope = element;
15182 else if (element == EL_EMC_LENSES)
15184 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15186 RedrawAllInvisibleElementsForLenses();
15188 else if (element == EL_EMC_MAGNIFIER)
15190 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15192 RedrawAllInvisibleElementsForMagnifier();
15194 else if (IS_DROPPABLE(element) ||
15195 IS_THROWABLE(element)) /* can be collected and dropped */
15199 if (collect_count == 0)
15200 player->inventory_infinite_element = element;
15202 for (i = 0; i < collect_count; i++)
15203 if (player->inventory_size < MAX_INVENTORY_SIZE)
15204 player->inventory_element[player->inventory_size++] = element;
15206 DrawGameDoorValues();
15208 else if (collect_count > 0)
15210 local_player->gems_still_needed -= collect_count;
15211 if (local_player->gems_still_needed < 0)
15212 local_player->gems_still_needed = 0;
15215 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15217 DisplayGameControlValues();
15219 DrawGameValue_Emeralds(local_player->gems_still_needed);
15223 RaiseScoreElement(element);
15224 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15227 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15228 player->index_bit, dig_side);
15230 if (mode == DF_SNAP)
15232 #if USE_NEW_SNAP_DELAY
15233 if (level.block_snap_field)
15234 setFieldForSnapping(x, y, element, move_direction);
15236 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15238 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15241 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15242 player->index_bit, dig_side);
15245 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15247 if (mode == DF_SNAP && element != EL_BD_ROCK)
15248 return MP_NO_ACTION;
15250 if (CAN_FALL(element) && dy)
15251 return MP_NO_ACTION;
15253 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15254 !(element == EL_SPRING && level.use_spring_bug))
15255 return MP_NO_ACTION;
15257 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15258 ((move_direction & MV_VERTICAL &&
15259 ((element_info[element].move_pattern & MV_LEFT &&
15260 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15261 (element_info[element].move_pattern & MV_RIGHT &&
15262 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15263 (move_direction & MV_HORIZONTAL &&
15264 ((element_info[element].move_pattern & MV_UP &&
15265 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15266 (element_info[element].move_pattern & MV_DOWN &&
15267 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15268 return MP_NO_ACTION;
15270 /* do not push elements already moving away faster than player */
15271 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15272 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15273 return MP_NO_ACTION;
15275 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15277 if (player->push_delay_value == -1 || !player_was_pushing)
15278 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15280 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15282 if (player->push_delay_value == -1)
15283 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15285 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15287 if (!player->is_pushing)
15288 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15291 player->is_pushing = TRUE;
15292 player->is_active = TRUE;
15294 if (!(IN_LEV_FIELD(nextx, nexty) &&
15295 (IS_FREE(nextx, nexty) ||
15296 (IS_SB_ELEMENT(element) &&
15297 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15298 (IS_CUSTOM_ELEMENT(element) &&
15299 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15300 return MP_NO_ACTION;
15302 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15303 return MP_NO_ACTION;
15305 if (player->push_delay == -1) /* new pushing; restart delay */
15306 player->push_delay = 0;
15308 if (player->push_delay < player->push_delay_value &&
15309 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15310 element != EL_SPRING && element != EL_BALLOON)
15312 /* make sure that there is no move delay before next try to push */
15313 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15314 player->move_delay = 0;
15316 return MP_NO_ACTION;
15319 if (IS_CUSTOM_ELEMENT(element) &&
15320 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15322 if (!DigFieldByCE(nextx, nexty, element))
15323 return MP_NO_ACTION;
15326 if (IS_SB_ELEMENT(element))
15328 if (element == EL_SOKOBAN_FIELD_FULL)
15330 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15331 local_player->sokobanfields_still_needed++;
15334 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15336 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15337 local_player->sokobanfields_still_needed--;
15340 Feld[x][y] = EL_SOKOBAN_OBJECT;
15342 if (Back[x][y] == Back[nextx][nexty])
15343 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15344 else if (Back[x][y] != 0)
15345 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15348 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15351 if (local_player->sokobanfields_still_needed == 0 &&
15352 game.emulation == EMU_SOKOBAN)
15354 PlayerWins(player);
15356 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15360 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15362 InitMovingField(x, y, move_direction);
15363 GfxAction[x][y] = ACTION_PUSHING;
15365 if (mode == DF_SNAP)
15366 ContinueMoving(x, y);
15368 MovPos[x][y] = (dx != 0 ? dx : dy);
15370 Pushed[x][y] = TRUE;
15371 Pushed[nextx][nexty] = TRUE;
15373 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15374 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15376 player->push_delay_value = -1; /* get new value later */
15378 /* check for element change _after_ element has been pushed */
15379 if (game.use_change_when_pushing_bug)
15381 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15382 player->index_bit, dig_side);
15383 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15384 player->index_bit, dig_side);
15387 else if (IS_SWITCHABLE(element))
15389 if (PLAYER_SWITCHING(player, x, y))
15391 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15392 player->index_bit, dig_side);
15397 player->is_switching = TRUE;
15398 player->switch_x = x;
15399 player->switch_y = y;
15401 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15403 if (element == EL_ROBOT_WHEEL)
15405 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15409 game.robot_wheel_active = TRUE;
15411 TEST_DrawLevelField(x, y);
15413 else if (element == EL_SP_TERMINAL)
15417 SCAN_PLAYFIELD(xx, yy)
15419 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15421 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15422 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15425 else if (IS_BELT_SWITCH(element))
15427 ToggleBeltSwitch(x, y);
15429 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15430 element == EL_SWITCHGATE_SWITCH_DOWN ||
15431 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15432 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15434 ToggleSwitchgateSwitch(x, y);
15436 else if (element == EL_LIGHT_SWITCH ||
15437 element == EL_LIGHT_SWITCH_ACTIVE)
15439 ToggleLightSwitch(x, y);
15441 else if (element == EL_TIMEGATE_SWITCH ||
15442 element == EL_DC_TIMEGATE_SWITCH)
15444 ActivateTimegateSwitch(x, y);
15446 else if (element == EL_BALLOON_SWITCH_LEFT ||
15447 element == EL_BALLOON_SWITCH_RIGHT ||
15448 element == EL_BALLOON_SWITCH_UP ||
15449 element == EL_BALLOON_SWITCH_DOWN ||
15450 element == EL_BALLOON_SWITCH_NONE ||
15451 element == EL_BALLOON_SWITCH_ANY)
15453 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15454 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15455 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15456 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15457 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15460 else if (element == EL_LAMP)
15462 Feld[x][y] = EL_LAMP_ACTIVE;
15463 local_player->lights_still_needed--;
15465 ResetGfxAnimation(x, y);
15466 TEST_DrawLevelField(x, y);
15468 else if (element == EL_TIME_ORB_FULL)
15470 Feld[x][y] = EL_TIME_ORB_EMPTY;
15472 if (level.time > 0 || level.use_time_orb_bug)
15474 TimeLeft += level.time_orb_time;
15477 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15479 DisplayGameControlValues();
15481 DrawGameValue_Time(TimeLeft);
15485 ResetGfxAnimation(x, y);
15486 TEST_DrawLevelField(x, y);
15488 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15489 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15493 game.ball_state = !game.ball_state;
15495 SCAN_PLAYFIELD(xx, yy)
15497 int e = Feld[xx][yy];
15499 if (game.ball_state)
15501 if (e == EL_EMC_MAGIC_BALL)
15502 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15503 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15504 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15508 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15509 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15510 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15511 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15516 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15517 player->index_bit, dig_side);
15519 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15520 player->index_bit, dig_side);
15522 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15523 player->index_bit, dig_side);
15529 if (!PLAYER_SWITCHING(player, x, y))
15531 player->is_switching = TRUE;
15532 player->switch_x = x;
15533 player->switch_y = y;
15535 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15536 player->index_bit, dig_side);
15537 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15538 player->index_bit, dig_side);
15540 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15541 player->index_bit, dig_side);
15542 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15543 player->index_bit, dig_side);
15546 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15547 player->index_bit, dig_side);
15548 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15549 player->index_bit, dig_side);
15551 return MP_NO_ACTION;
15554 player->push_delay = -1;
15556 if (is_player) /* function can also be called by EL_PENGUIN */
15558 if (Feld[x][y] != element) /* really digged/collected something */
15560 player->is_collecting = !player->is_digging;
15561 player->is_active = TRUE;
15568 static boolean DigFieldByCE(int x, int y, int digging_element)
15570 int element = Feld[x][y];
15572 if (!IS_FREE(x, y))
15574 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15575 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15578 /* no element can dig solid indestructible elements */
15579 if (IS_INDESTRUCTIBLE(element) &&
15580 !IS_DIGGABLE(element) &&
15581 !IS_COLLECTIBLE(element))
15584 if (AmoebaNr[x][y] &&
15585 (element == EL_AMOEBA_FULL ||
15586 element == EL_BD_AMOEBA ||
15587 element == EL_AMOEBA_GROWING))
15589 AmoebaCnt[AmoebaNr[x][y]]--;
15590 AmoebaCnt2[AmoebaNr[x][y]]--;
15593 if (IS_MOVING(x, y))
15594 RemoveMovingField(x, y);
15598 TEST_DrawLevelField(x, y);
15601 /* if digged element was about to explode, prevent the explosion */
15602 ExplodeField[x][y] = EX_TYPE_NONE;
15604 PlayLevelSoundAction(x, y, action);
15607 Store[x][y] = EL_EMPTY;
15610 /* this makes it possible to leave the removed element again */
15611 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15612 Store[x][y] = element;
15614 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15616 int move_leave_element = element_info[digging_element].move_leave_element;
15618 /* this makes it possible to leave the removed element again */
15619 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15620 element : move_leave_element);
15627 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15629 int jx = player->jx, jy = player->jy;
15630 int x = jx + dx, y = jy + dy;
15631 int snap_direction = (dx == -1 ? MV_LEFT :
15632 dx == +1 ? MV_RIGHT :
15634 dy == +1 ? MV_DOWN : MV_NONE);
15635 boolean can_continue_snapping = (level.continuous_snapping &&
15636 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15638 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15641 if (!player->active || !IN_LEV_FIELD(x, y))
15649 if (player->MovPos == 0)
15650 player->is_pushing = FALSE;
15652 player->is_snapping = FALSE;
15654 if (player->MovPos == 0)
15656 player->is_moving = FALSE;
15657 player->is_digging = FALSE;
15658 player->is_collecting = FALSE;
15664 #if USE_NEW_CONTINUOUS_SNAPPING
15665 /* prevent snapping with already pressed snap key when not allowed */
15666 if (player->is_snapping && !can_continue_snapping)
15669 if (player->is_snapping)
15673 player->MovDir = snap_direction;
15675 if (player->MovPos == 0)
15677 player->is_moving = FALSE;
15678 player->is_digging = FALSE;
15679 player->is_collecting = FALSE;
15682 player->is_dropping = FALSE;
15683 player->is_dropping_pressed = FALSE;
15684 player->drop_pressed_delay = 0;
15686 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15689 player->is_snapping = TRUE;
15690 player->is_active = TRUE;
15692 if (player->MovPos == 0)
15694 player->is_moving = FALSE;
15695 player->is_digging = FALSE;
15696 player->is_collecting = FALSE;
15699 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15700 TEST_DrawLevelField(player->last_jx, player->last_jy);
15702 TEST_DrawLevelField(x, y);
15707 static boolean DropElement(struct PlayerInfo *player)
15709 int old_element, new_element;
15710 int dropx = player->jx, dropy = player->jy;
15711 int drop_direction = player->MovDir;
15712 int drop_side = drop_direction;
15714 int drop_element = get_next_dropped_element(player);
15716 int drop_element = (player->inventory_size > 0 ?
15717 player->inventory_element[player->inventory_size - 1] :
15718 player->inventory_infinite_element != EL_UNDEFINED ?
15719 player->inventory_infinite_element :
15720 player->dynabombs_left > 0 ?
15721 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15725 player->is_dropping_pressed = TRUE;
15727 /* do not drop an element on top of another element; when holding drop key
15728 pressed without moving, dropped element must move away before the next
15729 element can be dropped (this is especially important if the next element
15730 is dynamite, which can be placed on background for historical reasons) */
15731 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15734 if (IS_THROWABLE(drop_element))
15736 dropx += GET_DX_FROM_DIR(drop_direction);
15737 dropy += GET_DY_FROM_DIR(drop_direction);
15739 if (!IN_LEV_FIELD(dropx, dropy))
15743 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15744 new_element = drop_element; /* default: no change when dropping */
15746 /* check if player is active, not moving and ready to drop */
15747 if (!player->active || player->MovPos || player->drop_delay > 0)
15750 /* check if player has anything that can be dropped */
15751 if (new_element == EL_UNDEFINED)
15754 /* check if drop key was pressed long enough for EM style dynamite */
15755 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15758 /* check if anything can be dropped at the current position */
15759 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15762 /* collected custom elements can only be dropped on empty fields */
15763 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15766 if (old_element != EL_EMPTY)
15767 Back[dropx][dropy] = old_element; /* store old element on this field */
15769 ResetGfxAnimation(dropx, dropy);
15770 ResetRandomAnimationValue(dropx, dropy);
15772 if (player->inventory_size > 0 ||
15773 player->inventory_infinite_element != EL_UNDEFINED)
15775 if (player->inventory_size > 0)
15777 player->inventory_size--;
15779 DrawGameDoorValues();
15781 if (new_element == EL_DYNAMITE)
15782 new_element = EL_DYNAMITE_ACTIVE;
15783 else if (new_element == EL_EM_DYNAMITE)
15784 new_element = EL_EM_DYNAMITE_ACTIVE;
15785 else if (new_element == EL_SP_DISK_RED)
15786 new_element = EL_SP_DISK_RED_ACTIVE;
15789 Feld[dropx][dropy] = new_element;
15791 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15792 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15793 el2img(Feld[dropx][dropy]), 0);
15795 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15797 /* needed if previous element just changed to "empty" in the last frame */
15798 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15800 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15801 player->index_bit, drop_side);
15802 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15804 player->index_bit, drop_side);
15806 TestIfElementTouchesCustomElement(dropx, dropy);
15808 else /* player is dropping a dyna bomb */
15810 player->dynabombs_left--;
15812 Feld[dropx][dropy] = new_element;
15814 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15815 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15816 el2img(Feld[dropx][dropy]), 0);
15818 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15821 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15822 InitField_WithBug1(dropx, dropy, FALSE);
15824 new_element = Feld[dropx][dropy]; /* element might have changed */
15826 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15827 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15829 int move_direction, nextx, nexty;
15831 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15832 MovDir[dropx][dropy] = drop_direction;
15834 move_direction = MovDir[dropx][dropy];
15835 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15836 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15838 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15840 #if USE_FIX_IMPACT_COLLISION
15841 /* do not cause impact style collision by dropping elements that can fall */
15842 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15844 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15848 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15849 player->is_dropping = TRUE;
15851 player->drop_pressed_delay = 0;
15852 player->is_dropping_pressed = FALSE;
15854 player->drop_x = dropx;
15855 player->drop_y = dropy;
15860 /* ------------------------------------------------------------------------- */
15861 /* game sound playing functions */
15862 /* ------------------------------------------------------------------------- */
15864 static int *loop_sound_frame = NULL;
15865 static int *loop_sound_volume = NULL;
15867 void InitPlayLevelSound()
15869 int num_sounds = getSoundListSize();
15871 checked_free(loop_sound_frame);
15872 checked_free(loop_sound_volume);
15874 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15875 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15878 static void PlayLevelSound(int x, int y, int nr)
15880 int sx = SCREENX(x), sy = SCREENY(y);
15881 int volume, stereo_position;
15882 int max_distance = 8;
15883 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15885 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15886 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15889 if (!IN_LEV_FIELD(x, y) ||
15890 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15891 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15894 volume = SOUND_MAX_VOLUME;
15896 if (!IN_SCR_FIELD(sx, sy))
15898 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15899 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15901 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15904 stereo_position = (SOUND_MAX_LEFT +
15905 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15906 (SCR_FIELDX + 2 * max_distance));
15908 if (IS_LOOP_SOUND(nr))
15910 /* This assures that quieter loop sounds do not overwrite louder ones,
15911 while restarting sound volume comparison with each new game frame. */
15913 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15916 loop_sound_volume[nr] = volume;
15917 loop_sound_frame[nr] = FrameCounter;
15920 PlaySoundExt(nr, volume, stereo_position, type);
15923 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15925 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15926 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15927 y < LEVELY(BY1) ? LEVELY(BY1) :
15928 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15932 static void PlayLevelSoundAction(int x, int y, int action)
15934 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15937 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15939 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15941 if (sound_effect != SND_UNDEFINED)
15942 PlayLevelSound(x, y, sound_effect);
15945 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15948 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15950 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15951 PlayLevelSound(x, y, sound_effect);
15954 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15956 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15958 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15959 PlayLevelSound(x, y, sound_effect);
15962 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15964 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15966 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15967 StopSound(sound_effect);
15970 static void PlayLevelMusic()
15972 if (levelset.music[level_nr] != MUS_UNDEFINED)
15973 PlayMusic(levelset.music[level_nr]); /* from config file */
15975 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15978 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15980 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15981 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15982 int x = xx - 1 - offset;
15983 int y = yy - 1 - offset;
15988 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15992 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15996 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16000 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16004 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16008 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16012 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16015 case SAMPLE_android_clone:
16016 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16019 case SAMPLE_android_move:
16020 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16023 case SAMPLE_spring:
16024 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16028 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16032 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16035 case SAMPLE_eater_eat:
16036 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16040 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16043 case SAMPLE_collect:
16044 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16047 case SAMPLE_diamond:
16048 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16051 case SAMPLE_squash:
16052 /* !!! CHECK THIS !!! */
16054 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16056 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16060 case SAMPLE_wonderfall:
16061 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16065 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16069 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16073 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16077 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16081 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16085 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16088 case SAMPLE_wonder:
16089 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16093 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16096 case SAMPLE_exit_open:
16097 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16100 case SAMPLE_exit_leave:
16101 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16104 case SAMPLE_dynamite:
16105 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16109 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16117 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16121 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16125 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16129 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16133 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16139 void ChangeTime(int value)
16141 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16145 /* EMC game engine uses value from time counter of RND game engine */
16146 level.native_em_level->lev->time = *time;
16148 DrawGameValue_Time(*time);
16151 void RaiseScore(int value)
16153 /* EMC game engine and RND game engine have separate score counters */
16154 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16155 &level.native_em_level->lev->score : &local_player->score);
16159 DrawGameValue_Score(*score);
16163 void RaiseScore(int value)
16165 local_player->score += value;
16168 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16170 DisplayGameControlValues();
16172 DrawGameValue_Score(local_player->score);
16176 void RaiseScoreElement(int element)
16181 case EL_BD_DIAMOND:
16182 case EL_EMERALD_YELLOW:
16183 case EL_EMERALD_RED:
16184 case EL_EMERALD_PURPLE:
16185 case EL_SP_INFOTRON:
16186 RaiseScore(level.score[SC_EMERALD]);
16189 RaiseScore(level.score[SC_DIAMOND]);
16192 RaiseScore(level.score[SC_CRYSTAL]);
16195 RaiseScore(level.score[SC_PEARL]);
16198 case EL_BD_BUTTERFLY:
16199 case EL_SP_ELECTRON:
16200 RaiseScore(level.score[SC_BUG]);
16203 case EL_BD_FIREFLY:
16204 case EL_SP_SNIKSNAK:
16205 RaiseScore(level.score[SC_SPACESHIP]);
16208 case EL_DARK_YAMYAM:
16209 RaiseScore(level.score[SC_YAMYAM]);
16212 RaiseScore(level.score[SC_ROBOT]);
16215 RaiseScore(level.score[SC_PACMAN]);
16218 RaiseScore(level.score[SC_NUT]);
16221 case EL_EM_DYNAMITE:
16222 case EL_SP_DISK_RED:
16223 case EL_DYNABOMB_INCREASE_NUMBER:
16224 case EL_DYNABOMB_INCREASE_SIZE:
16225 case EL_DYNABOMB_INCREASE_POWER:
16226 RaiseScore(level.score[SC_DYNAMITE]);
16228 case EL_SHIELD_NORMAL:
16229 case EL_SHIELD_DEADLY:
16230 RaiseScore(level.score[SC_SHIELD]);
16232 case EL_EXTRA_TIME:
16233 RaiseScore(level.extra_time_score);
16247 case EL_DC_KEY_WHITE:
16248 RaiseScore(level.score[SC_KEY]);
16251 RaiseScore(element_info[element].collect_score);
16256 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16258 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16260 #if defined(NETWORK_AVALIABLE)
16261 if (options.network)
16262 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16271 FadeSkipNextFadeIn();
16273 fading = fading_none;
16277 OpenDoor(DOOR_CLOSE_1);
16280 game_status = GAME_MODE_MAIN;
16283 DrawAndFadeInMainMenu(REDRAW_FIELD);
16291 FadeOut(REDRAW_FIELD);
16294 game_status = GAME_MODE_MAIN;
16296 DrawAndFadeInMainMenu(REDRAW_FIELD);
16300 else /* continue playing the game */
16302 if (tape.playing && tape.deactivate_display)
16303 TapeDeactivateDisplayOff(TRUE);
16305 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16307 if (tape.playing && tape.deactivate_display)
16308 TapeDeactivateDisplayOn();
16312 void RequestQuitGame(boolean ask_if_really_quit)
16314 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16315 boolean skip_request = AllPlayersGone || quick_quit;
16317 RequestQuitGameExt(skip_request, quick_quit,
16318 "Do you really want to quit the game ?");
16322 /* ------------------------------------------------------------------------- */
16323 /* random generator functions */
16324 /* ------------------------------------------------------------------------- */
16326 unsigned int InitEngineRandom_RND(long seed)
16328 game.num_random_calls = 0;
16331 unsigned int rnd_seed = InitEngineRandom(seed);
16333 printf("::: START RND: %d\n", rnd_seed);
16338 return InitEngineRandom(seed);
16344 unsigned int RND(int max)
16348 game.num_random_calls++;
16350 return GetEngineRandom(max);
16357 /* ------------------------------------------------------------------------- */
16358 /* game engine snapshot handling functions */
16359 /* ------------------------------------------------------------------------- */
16361 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16363 struct EngineSnapshotInfo
16365 /* runtime values for custom element collect score */
16366 int collect_score[NUM_CUSTOM_ELEMENTS];
16368 /* runtime values for group element choice position */
16369 int choice_pos[NUM_GROUP_ELEMENTS];
16371 /* runtime values for belt position animations */
16372 int belt_graphic[4 * NUM_BELT_PARTS];
16373 int belt_anim_mode[4 * NUM_BELT_PARTS];
16376 struct EngineSnapshotNodeInfo
16383 static struct EngineSnapshotInfo engine_snapshot_rnd;
16384 static ListNode *engine_snapshot_list = NULL;
16385 static char *snapshot_level_identifier = NULL;
16386 static int snapshot_level_nr = -1;
16388 void FreeEngineSnapshot()
16390 while (engine_snapshot_list != NULL)
16391 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16394 setString(&snapshot_level_identifier, NULL);
16395 snapshot_level_nr = -1;
16398 static void SaveEngineSnapshotValues_RND()
16400 static int belt_base_active_element[4] =
16402 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16403 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16404 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16405 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16409 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16411 int element = EL_CUSTOM_START + i;
16413 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16416 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16418 int element = EL_GROUP_START + i;
16420 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16423 for (i = 0; i < 4; i++)
16425 for (j = 0; j < NUM_BELT_PARTS; j++)
16427 int element = belt_base_active_element[i] + j;
16428 int graphic = el2img(element);
16429 int anim_mode = graphic_info[graphic].anim_mode;
16431 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16432 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16437 static void LoadEngineSnapshotValues_RND()
16439 unsigned long num_random_calls = game.num_random_calls;
16442 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16444 int element = EL_CUSTOM_START + i;
16446 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16449 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16451 int element = EL_GROUP_START + i;
16453 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16456 for (i = 0; i < 4; i++)
16458 for (j = 0; j < NUM_BELT_PARTS; j++)
16460 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16461 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16463 graphic_info[graphic].anim_mode = anim_mode;
16467 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16469 InitRND(tape.random_seed);
16470 for (i = 0; i < num_random_calls; i++)
16474 if (game.num_random_calls != num_random_calls)
16476 Error(ERR_INFO, "number of random calls out of sync");
16477 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16478 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16479 Error(ERR_EXIT, "this should not happen -- please debug");
16483 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16485 struct EngineSnapshotNodeInfo *bi =
16486 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16488 bi->buffer_orig = buffer;
16489 bi->buffer_copy = checked_malloc(size);
16492 memcpy(bi->buffer_copy, buffer, size);
16494 addNodeToList(&engine_snapshot_list, NULL, bi);
16497 void SaveEngineSnapshot()
16499 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16501 if (level_editor_test_game) /* do not save snapshots from editor */
16504 /* copy some special values to a structure better suited for the snapshot */
16506 SaveEngineSnapshotValues_RND();
16507 SaveEngineSnapshotValues_EM();
16509 /* save values stored in special snapshot structure */
16511 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16514 /* save further RND engine values */
16516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16517 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16521 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16522 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16527 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16532 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16580 /* save level identification information */
16582 setString(&snapshot_level_identifier, leveldir_current->identifier);
16583 snapshot_level_nr = level_nr;
16586 ListNode *node = engine_snapshot_list;
16589 while (node != NULL)
16591 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16596 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16600 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16602 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16605 void LoadEngineSnapshot()
16607 ListNode *node = engine_snapshot_list;
16609 if (engine_snapshot_list == NULL)
16612 while (node != NULL)
16614 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16619 /* restore special values from snapshot structure */
16621 LoadEngineSnapshotValues_RND();
16622 LoadEngineSnapshotValues_EM();
16625 boolean CheckEngineSnapshot()
16627 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16628 snapshot_level_nr == level_nr);
16632 /* ---------- new game button stuff ---------------------------------------- */
16634 /* graphic position values for game buttons */
16635 #define GAME_BUTTON_XSIZE 30
16636 #define GAME_BUTTON_YSIZE 30
16637 #define GAME_BUTTON_XPOS 5
16638 #define GAME_BUTTON_YPOS 215
16639 #define SOUND_BUTTON_XPOS 5
16640 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16642 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16643 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16644 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16645 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16646 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16647 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16655 } gamebutton_info[NUM_GAME_BUTTONS] =
16659 &game.button.stop.x, &game.button.stop.y,
16660 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16665 &game.button.pause.x, &game.button.pause.y,
16666 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16667 GAME_CTRL_ID_PAUSE,
16671 &game.button.play.x, &game.button.play.y,
16672 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16677 &game.button.sound_music.x, &game.button.sound_music.y,
16678 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16679 SOUND_CTRL_ID_MUSIC,
16680 "background music on/off"
16683 &game.button.sound_loops.x, &game.button.sound_loops.y,
16684 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16685 SOUND_CTRL_ID_LOOPS,
16686 "sound loops on/off"
16689 &game.button.sound_simple.x,&game.button.sound_simple.y,
16690 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16691 SOUND_CTRL_ID_SIMPLE,
16692 "normal sounds on/off"
16696 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16701 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16702 GAME_CTRL_ID_PAUSE,
16706 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16711 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16712 SOUND_CTRL_ID_MUSIC,
16713 "background music on/off"
16716 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16717 SOUND_CTRL_ID_LOOPS,
16718 "sound loops on/off"
16721 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16722 SOUND_CTRL_ID_SIMPLE,
16723 "normal sounds on/off"
16728 void CreateGameButtons()
16732 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16734 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16735 struct GadgetInfo *gi;
16738 unsigned long event_mask;
16740 int gd_xoffset, gd_yoffset;
16741 int gd_x1, gd_x2, gd_y1, gd_y2;
16744 x = DX + *gamebutton_info[i].x;
16745 y = DY + *gamebutton_info[i].y;
16746 gd_xoffset = gamebutton_info[i].gd_x;
16747 gd_yoffset = gamebutton_info[i].gd_y;
16748 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16749 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16751 if (id == GAME_CTRL_ID_STOP ||
16752 id == GAME_CTRL_ID_PAUSE ||
16753 id == GAME_CTRL_ID_PLAY)
16755 button_type = GD_TYPE_NORMAL_BUTTON;
16757 event_mask = GD_EVENT_RELEASED;
16758 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16759 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16763 button_type = GD_TYPE_CHECK_BUTTON;
16765 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16766 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16767 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16768 event_mask = GD_EVENT_PRESSED;
16769 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16770 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16773 gi = CreateGadget(GDI_CUSTOM_ID, id,
16774 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16779 GDI_X, DX + gd_xoffset,
16780 GDI_Y, DY + gd_yoffset,
16782 GDI_WIDTH, GAME_BUTTON_XSIZE,
16783 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16784 GDI_TYPE, button_type,
16785 GDI_STATE, GD_BUTTON_UNPRESSED,
16786 GDI_CHECKED, checked,
16787 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16788 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16789 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16790 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16791 GDI_DIRECT_DRAW, FALSE,
16792 GDI_EVENT_MASK, event_mask,
16793 GDI_CALLBACK_ACTION, HandleGameButtons,
16797 Error(ERR_EXIT, "cannot create gadget");
16799 game_gadget[id] = gi;
16803 void FreeGameButtons()
16807 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16808 FreeGadget(game_gadget[i]);
16811 static void MapGameButtons()
16815 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16816 MapGadget(game_gadget[i]);
16819 void UnmapGameButtons()
16823 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16824 UnmapGadget(game_gadget[i]);
16827 void RedrawGameButtons()
16831 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16832 RedrawGadget(game_gadget[i]);
16835 static void HandleGameButtons(struct GadgetInfo *gi)
16837 int id = gi->custom_id;
16839 if (game_status != GAME_MODE_PLAYING)
16844 case GAME_CTRL_ID_STOP:
16848 RequestQuitGame(TRUE);
16851 case GAME_CTRL_ID_PAUSE:
16852 if (options.network)
16854 #if defined(NETWORK_AVALIABLE)
16856 SendToServer_ContinuePlaying();
16858 SendToServer_PausePlaying();
16862 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16865 case GAME_CTRL_ID_PLAY:
16868 #if defined(NETWORK_AVALIABLE)
16869 if (options.network)
16870 SendToServer_ContinuePlaying();
16874 tape.pausing = FALSE;
16875 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16880 case SOUND_CTRL_ID_MUSIC:
16881 if (setup.sound_music)
16883 setup.sound_music = FALSE;
16886 else if (audio.music_available)
16888 setup.sound = setup.sound_music = TRUE;
16890 SetAudioMode(setup.sound);
16896 case SOUND_CTRL_ID_LOOPS:
16897 if (setup.sound_loops)
16898 setup.sound_loops = FALSE;
16899 else if (audio.loops_available)
16901 setup.sound = setup.sound_loops = TRUE;
16902 SetAudioMode(setup.sound);
16906 case SOUND_CTRL_ID_SIMPLE:
16907 if (setup.sound_simple)
16908 setup.sound_simple = FALSE;
16909 else if (audio.sound_available)
16911 setup.sound = setup.sound_simple = TRUE;
16912 SetAudioMode(setup.sound);