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;
12040 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12042 if (game_sp_info.LevelSolved) /* game won */
12044 PlayerWins(local_player);
12046 AllPlayersGone = TRUE;
12049 if (game_sp_info.GameOver) /* game lost */
12050 AllPlayersGone = TRUE;
12053 if (TimeFrames >= FRAMES_PER_SECOND)
12058 for (i = 0; i < MAX_PLAYERS; i++)
12060 struct PlayerInfo *player = &stored_player[i];
12062 if (SHIELD_ON(player))
12064 player->shield_normal_time_left--;
12066 if (player->shield_deadly_time_left > 0)
12067 player->shield_deadly_time_left--;
12071 if (!local_player->LevelSolved && !level.use_step_counter)
12079 if (TimeLeft <= 10 && setup.time_limit)
12080 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12083 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12085 DisplayGameControlValues();
12087 DrawGameValue_Time(TimeLeft);
12090 if (!TimeLeft && setup.time_limit)
12092 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12093 level.native_em_level->lev->killed_out_of_time = TRUE;
12095 for (i = 0; i < MAX_PLAYERS; i++)
12096 KillPlayer(&stored_player[i]);
12100 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12102 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12104 DisplayGameControlValues();
12107 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12108 DrawGameValue_Time(TimePlayed);
12111 level.native_em_level->lev->time =
12112 (level.time == 0 ? TimePlayed : TimeLeft);
12115 if (tape.recording || tape.playing)
12116 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12120 UpdateAndDisplayGameControlValues();
12122 UpdateGameDoorValues();
12123 DrawGameDoorValues();
12127 void AdvanceFrameAndPlayerCounters(int player_nr)
12131 /* advance frame counters (global frame counter and time frame counter) */
12135 /* advance player counters (counters for move delay, move animation etc.) */
12136 for (i = 0; i < MAX_PLAYERS; i++)
12138 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12139 int move_delay_value = stored_player[i].move_delay_value;
12140 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12142 if (!advance_player_counters) /* not all players may be affected */
12145 #if USE_NEW_PLAYER_ANIM
12146 if (move_frames == 0) /* less than one move per game frame */
12148 int stepsize = TILEX / move_delay_value;
12149 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12150 int count = (stored_player[i].is_moving ?
12151 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12153 if (count % delay == 0)
12158 stored_player[i].Frame += move_frames;
12160 if (stored_player[i].MovPos != 0)
12161 stored_player[i].StepFrame += move_frames;
12163 if (stored_player[i].move_delay > 0)
12164 stored_player[i].move_delay--;
12166 /* due to bugs in previous versions, counter must count up, not down */
12167 if (stored_player[i].push_delay != -1)
12168 stored_player[i].push_delay++;
12170 if (stored_player[i].drop_delay > 0)
12171 stored_player[i].drop_delay--;
12173 if (stored_player[i].is_dropping_pressed)
12174 stored_player[i].drop_pressed_delay++;
12178 void StartGameActions(boolean init_network_game, boolean record_tape,
12181 unsigned long new_random_seed = InitRND(random_seed);
12184 TapeStartRecording(new_random_seed);
12186 #if defined(NETWORK_AVALIABLE)
12187 if (init_network_game)
12189 SendToServer_StartPlaying();
12200 static unsigned long game_frame_delay = 0;
12201 unsigned long game_frame_delay_value;
12202 byte *recorded_player_action;
12203 byte summarized_player_action = 0;
12204 byte tape_action[MAX_PLAYERS];
12207 /* detect endless loops, caused by custom element programming */
12208 if (recursion_loop_detected && recursion_loop_depth == 0)
12210 char *message = getStringCat3("Internal Error ! Element ",
12211 EL_NAME(recursion_loop_element),
12212 " caused endless loop ! Quit the game ?");
12214 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12215 EL_NAME(recursion_loop_element));
12217 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12219 recursion_loop_detected = FALSE; /* if game should be continued */
12226 if (game.restart_level)
12227 StartGameActions(options.network, setup.autorecord, level.random_seed);
12229 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12231 if (level.native_em_level->lev->home == 0) /* all players at home */
12233 PlayerWins(local_player);
12235 AllPlayersGone = TRUE;
12237 level.native_em_level->lev->home = -1;
12240 if (level.native_em_level->ply[0]->alive == 0 &&
12241 level.native_em_level->ply[1]->alive == 0 &&
12242 level.native_em_level->ply[2]->alive == 0 &&
12243 level.native_em_level->ply[3]->alive == 0) /* all dead */
12244 AllPlayersGone = TRUE;
12247 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12250 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12253 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12256 game_frame_delay_value =
12257 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12259 if (tape.playing && tape.warp_forward && !tape.pausing)
12260 game_frame_delay_value = 0;
12262 /* ---------- main game synchronization point ---------- */
12264 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12266 if (network_playing && !network_player_action_received)
12268 /* try to get network player actions in time */
12270 #if defined(NETWORK_AVALIABLE)
12271 /* last chance to get network player actions without main loop delay */
12272 HandleNetworking();
12275 /* game was quit by network peer */
12276 if (game_status != GAME_MODE_PLAYING)
12279 if (!network_player_action_received)
12280 return; /* failed to get network player actions in time */
12282 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12288 /* at this point we know that we really continue executing the game */
12290 network_player_action_received = FALSE;
12292 /* when playing tape, read previously recorded player input from tape data */
12293 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12296 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12301 if (tape.set_centered_player)
12303 game.centered_player_nr_next = tape.centered_player_nr_next;
12304 game.set_centered_player = TRUE;
12307 for (i = 0; i < MAX_PLAYERS; i++)
12309 summarized_player_action |= stored_player[i].action;
12311 if (!network_playing)
12312 stored_player[i].effective_action = stored_player[i].action;
12315 #if defined(NETWORK_AVALIABLE)
12316 if (network_playing)
12317 SendToServer_MovePlayer(summarized_player_action);
12320 if (!options.network && !setup.team_mode)
12321 local_player->effective_action = summarized_player_action;
12323 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12325 for (i = 0; i < MAX_PLAYERS; i++)
12326 stored_player[i].effective_action =
12327 (i == game.centered_player_nr ? summarized_player_action : 0);
12330 if (recorded_player_action != NULL)
12331 for (i = 0; i < MAX_PLAYERS; i++)
12332 stored_player[i].effective_action = recorded_player_action[i];
12334 for (i = 0; i < MAX_PLAYERS; i++)
12336 tape_action[i] = stored_player[i].effective_action;
12338 /* (this can only happen in the R'n'D game engine) */
12339 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12340 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12343 /* only record actions from input devices, but not programmed actions */
12344 if (tape.recording)
12345 TapeRecordAction(tape_action);
12347 #if USE_NEW_PLAYER_ASSIGNMENTS
12349 byte mapped_action[MAX_PLAYERS];
12351 for (i = 0; i < MAX_PLAYERS; i++)
12352 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12354 for (i = 0; i < MAX_PLAYERS; i++)
12355 stored_player[i].effective_action = mapped_action[i];
12359 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12361 GameActions_EM_Main();
12363 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12365 GameActions_SP_Main();
12373 void GameActions_EM_Main()
12375 byte effective_action[MAX_PLAYERS];
12376 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12379 for (i = 0; i < MAX_PLAYERS; i++)
12380 effective_action[i] = stored_player[i].effective_action;
12382 GameActions_EM(effective_action, warp_mode);
12386 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12389 void GameActions_SP_Main()
12391 byte effective_action[MAX_PLAYERS];
12392 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12395 for (i = 0; i < MAX_PLAYERS; i++)
12396 effective_action[i] = stored_player[i].effective_action;
12398 GameActions_SP(effective_action, warp_mode);
12402 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12405 void GameActions_RND()
12407 int magic_wall_x = 0, magic_wall_y = 0;
12408 int i, x, y, element, graphic;
12410 InitPlayfieldScanModeVars();
12412 #if USE_ONE_MORE_CHANGE_PER_FRAME
12413 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12415 SCAN_PLAYFIELD(x, y)
12417 ChangeCount[x][y] = 0;
12418 ChangeEvent[x][y] = -1;
12423 if (game.set_centered_player)
12425 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12427 /* switching to "all players" only possible if all players fit to screen */
12428 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12430 game.centered_player_nr_next = game.centered_player_nr;
12431 game.set_centered_player = FALSE;
12434 /* do not switch focus to non-existing (or non-active) player */
12435 if (game.centered_player_nr_next >= 0 &&
12436 !stored_player[game.centered_player_nr_next].active)
12438 game.centered_player_nr_next = game.centered_player_nr;
12439 game.set_centered_player = FALSE;
12443 if (game.set_centered_player &&
12444 ScreenMovPos == 0) /* screen currently aligned at tile position */
12448 if (game.centered_player_nr_next == -1)
12450 setScreenCenteredToAllPlayers(&sx, &sy);
12454 sx = stored_player[game.centered_player_nr_next].jx;
12455 sy = stored_player[game.centered_player_nr_next].jy;
12458 game.centered_player_nr = game.centered_player_nr_next;
12459 game.set_centered_player = FALSE;
12461 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12462 DrawGameDoorValues();
12465 for (i = 0; i < MAX_PLAYERS; i++)
12467 int actual_player_action = stored_player[i].effective_action;
12470 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12471 - rnd_equinox_tetrachloride 048
12472 - rnd_equinox_tetrachloride_ii 096
12473 - rnd_emanuel_schmieg 002
12474 - doctor_sloan_ww 001, 020
12476 if (stored_player[i].MovPos == 0)
12477 CheckGravityMovement(&stored_player[i]);
12480 /* overwrite programmed action with tape action */
12481 if (stored_player[i].programmed_action)
12482 actual_player_action = stored_player[i].programmed_action;
12484 PlayerActions(&stored_player[i], actual_player_action);
12486 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12489 ScrollScreen(NULL, SCROLL_GO_ON);
12491 /* for backwards compatibility, the following code emulates a fixed bug that
12492 occured when pushing elements (causing elements that just made their last
12493 pushing step to already (if possible) make their first falling step in the
12494 same game frame, which is bad); this code is also needed to use the famous
12495 "spring push bug" which is used in older levels and might be wanted to be
12496 used also in newer levels, but in this case the buggy pushing code is only
12497 affecting the "spring" element and no other elements */
12499 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12501 for (i = 0; i < MAX_PLAYERS; i++)
12503 struct PlayerInfo *player = &stored_player[i];
12504 int x = player->jx;
12505 int y = player->jy;
12507 if (player->active && player->is_pushing && player->is_moving &&
12509 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12510 Feld[x][y] == EL_SPRING))
12512 ContinueMoving(x, y);
12514 /* continue moving after pushing (this is actually a bug) */
12515 if (!IS_MOVING(x, y))
12516 Stop[x][y] = FALSE;
12522 debug_print_timestamp(0, "start main loop profiling");
12525 SCAN_PLAYFIELD(x, y)
12527 ChangeCount[x][y] = 0;
12528 ChangeEvent[x][y] = -1;
12530 /* this must be handled before main playfield loop */
12531 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12534 if (MovDelay[x][y] <= 0)
12538 #if USE_NEW_SNAP_DELAY
12539 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12542 if (MovDelay[x][y] <= 0)
12545 TEST_DrawLevelField(x, y);
12547 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12553 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12555 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12556 printf("GameActions(): This should never happen!\n");
12558 ChangePage[x][y] = -1;
12562 Stop[x][y] = FALSE;
12563 if (WasJustMoving[x][y] > 0)
12564 WasJustMoving[x][y]--;
12565 if (WasJustFalling[x][y] > 0)
12566 WasJustFalling[x][y]--;
12567 if (CheckCollision[x][y] > 0)
12568 CheckCollision[x][y]--;
12569 if (CheckImpact[x][y] > 0)
12570 CheckImpact[x][y]--;
12574 /* reset finished pushing action (not done in ContinueMoving() to allow
12575 continuous pushing animation for elements with zero push delay) */
12576 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12578 ResetGfxAnimation(x, y);
12579 TEST_DrawLevelField(x, y);
12583 if (IS_BLOCKED(x, y))
12587 Blocked2Moving(x, y, &oldx, &oldy);
12588 if (!IS_MOVING(oldx, oldy))
12590 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12591 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12592 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12593 printf("GameActions(): This should never happen!\n");
12600 debug_print_timestamp(0, "- time for pre-main loop:");
12603 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12604 SCAN_PLAYFIELD(x, y)
12606 element = Feld[x][y];
12607 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12612 int element2 = element;
12613 int graphic2 = graphic;
12615 int element2 = Feld[x][y];
12616 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12618 int last_gfx_frame = GfxFrame[x][y];
12620 if (graphic_info[graphic2].anim_global_sync)
12621 GfxFrame[x][y] = FrameCounter;
12622 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12623 GfxFrame[x][y] = CustomValue[x][y];
12624 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12625 GfxFrame[x][y] = element_info[element2].collect_score;
12626 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12627 GfxFrame[x][y] = ChangeDelay[x][y];
12629 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12630 DrawLevelGraphicAnimation(x, y, graphic2);
12633 ResetGfxFrame(x, y, TRUE);
12637 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12638 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12639 ResetRandomAnimationValue(x, y);
12643 SetRandomAnimationValue(x, y);
12647 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12650 #endif // -------------------- !!! TEST ONLY !!! --------------------
12653 debug_print_timestamp(0, "- time for TEST loop: -->");
12656 SCAN_PLAYFIELD(x, y)
12658 element = Feld[x][y];
12659 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12661 ResetGfxFrame(x, y, TRUE);
12663 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12664 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12665 ResetRandomAnimationValue(x, y);
12667 SetRandomAnimationValue(x, y);
12669 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12671 if (IS_INACTIVE(element))
12673 if (IS_ANIMATED(graphic))
12674 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12679 /* this may take place after moving, so 'element' may have changed */
12680 if (IS_CHANGING(x, y) &&
12681 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12683 int page = element_info[element].event_page_nr[CE_DELAY];
12686 HandleElementChange(x, y, page);
12688 if (CAN_CHANGE(element))
12689 HandleElementChange(x, y, page);
12691 if (HAS_ACTION(element))
12692 ExecuteCustomElementAction(x, y, element, page);
12695 element = Feld[x][y];
12696 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12699 #if 0 // ---------------------------------------------------------------------
12701 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12705 element = Feld[x][y];
12706 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12708 if (IS_ANIMATED(graphic) &&
12709 !IS_MOVING(x, y) &&
12711 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12713 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12714 TEST_DrawTwinkleOnField(x, y);
12716 else if (IS_MOVING(x, y))
12717 ContinueMoving(x, y);
12724 case EL_EM_EXIT_OPEN:
12725 case EL_SP_EXIT_OPEN:
12726 case EL_STEEL_EXIT_OPEN:
12727 case EL_EM_STEEL_EXIT_OPEN:
12728 case EL_SP_TERMINAL:
12729 case EL_SP_TERMINAL_ACTIVE:
12730 case EL_EXTRA_TIME:
12731 case EL_SHIELD_NORMAL:
12732 case EL_SHIELD_DEADLY:
12733 if (IS_ANIMATED(graphic))
12734 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12737 case EL_DYNAMITE_ACTIVE:
12738 case EL_EM_DYNAMITE_ACTIVE:
12739 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12740 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12741 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12742 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12743 case EL_SP_DISK_RED_ACTIVE:
12744 CheckDynamite(x, y);
12747 case EL_AMOEBA_GROWING:
12748 AmoebeWaechst(x, y);
12751 case EL_AMOEBA_SHRINKING:
12752 AmoebaDisappearing(x, y);
12755 #if !USE_NEW_AMOEBA_CODE
12756 case EL_AMOEBA_WET:
12757 case EL_AMOEBA_DRY:
12758 case EL_AMOEBA_FULL:
12760 case EL_EMC_DRIPPER:
12761 AmoebeAbleger(x, y);
12765 case EL_GAME_OF_LIFE:
12770 case EL_EXIT_CLOSED:
12774 case EL_EM_EXIT_CLOSED:
12778 case EL_STEEL_EXIT_CLOSED:
12779 CheckExitSteel(x, y);
12782 case EL_EM_STEEL_EXIT_CLOSED:
12783 CheckExitSteelEM(x, y);
12786 case EL_SP_EXIT_CLOSED:
12790 case EL_EXPANDABLE_WALL_GROWING:
12791 case EL_EXPANDABLE_STEELWALL_GROWING:
12792 MauerWaechst(x, y);
12795 case EL_EXPANDABLE_WALL:
12796 case EL_EXPANDABLE_WALL_HORIZONTAL:
12797 case EL_EXPANDABLE_WALL_VERTICAL:
12798 case EL_EXPANDABLE_WALL_ANY:
12799 case EL_BD_EXPANDABLE_WALL:
12800 MauerAbleger(x, y);
12803 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12804 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12805 case EL_EXPANDABLE_STEELWALL_ANY:
12806 MauerAblegerStahl(x, y);
12810 CheckForDragon(x, y);
12816 case EL_ELEMENT_SNAPPING:
12817 case EL_DIAGONAL_SHRINKING:
12818 case EL_DIAGONAL_GROWING:
12821 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12823 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12828 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12829 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12834 #else // ---------------------------------------------------------------------
12836 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12840 element = Feld[x][y];
12841 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12843 if (IS_ANIMATED(graphic) &&
12844 !IS_MOVING(x, y) &&
12846 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12848 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12849 TEST_DrawTwinkleOnField(x, y);
12851 else if ((element == EL_ACID ||
12852 element == EL_EXIT_OPEN ||
12853 element == EL_EM_EXIT_OPEN ||
12854 element == EL_SP_EXIT_OPEN ||
12855 element == EL_STEEL_EXIT_OPEN ||
12856 element == EL_EM_STEEL_EXIT_OPEN ||
12857 element == EL_SP_TERMINAL ||
12858 element == EL_SP_TERMINAL_ACTIVE ||
12859 element == EL_EXTRA_TIME ||
12860 element == EL_SHIELD_NORMAL ||
12861 element == EL_SHIELD_DEADLY) &&
12862 IS_ANIMATED(graphic))
12863 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12864 else if (IS_MOVING(x, y))
12865 ContinueMoving(x, y);
12866 else if (IS_ACTIVE_BOMB(element))
12867 CheckDynamite(x, y);
12868 else if (element == EL_AMOEBA_GROWING)
12869 AmoebeWaechst(x, y);
12870 else if (element == EL_AMOEBA_SHRINKING)
12871 AmoebaDisappearing(x, y);
12873 #if !USE_NEW_AMOEBA_CODE
12874 else if (IS_AMOEBALIVE(element))
12875 AmoebeAbleger(x, y);
12878 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12880 else if (element == EL_EXIT_CLOSED)
12882 else if (element == EL_EM_EXIT_CLOSED)
12884 else if (element == EL_STEEL_EXIT_CLOSED)
12885 CheckExitSteel(x, y);
12886 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12887 CheckExitSteelEM(x, y);
12888 else if (element == EL_SP_EXIT_CLOSED)
12890 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12891 element == EL_EXPANDABLE_STEELWALL_GROWING)
12892 MauerWaechst(x, y);
12893 else if (element == EL_EXPANDABLE_WALL ||
12894 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12895 element == EL_EXPANDABLE_WALL_VERTICAL ||
12896 element == EL_EXPANDABLE_WALL_ANY ||
12897 element == EL_BD_EXPANDABLE_WALL)
12898 MauerAbleger(x, y);
12899 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12900 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12901 element == EL_EXPANDABLE_STEELWALL_ANY)
12902 MauerAblegerStahl(x, y);
12903 else if (element == EL_FLAMES)
12904 CheckForDragon(x, y);
12905 else if (element == EL_EXPLOSION)
12906 ; /* drawing of correct explosion animation is handled separately */
12907 else if (element == EL_ELEMENT_SNAPPING ||
12908 element == EL_DIAGONAL_SHRINKING ||
12909 element == EL_DIAGONAL_GROWING)
12911 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12913 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12915 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12916 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12918 #endif // ---------------------------------------------------------------------
12920 if (IS_BELT_ACTIVE(element))
12921 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12923 if (game.magic_wall_active)
12925 int jx = local_player->jx, jy = local_player->jy;
12927 /* play the element sound at the position nearest to the player */
12928 if ((element == EL_MAGIC_WALL_FULL ||
12929 element == EL_MAGIC_WALL_ACTIVE ||
12930 element == EL_MAGIC_WALL_EMPTYING ||
12931 element == EL_BD_MAGIC_WALL_FULL ||
12932 element == EL_BD_MAGIC_WALL_ACTIVE ||
12933 element == EL_BD_MAGIC_WALL_EMPTYING ||
12934 element == EL_DC_MAGIC_WALL_FULL ||
12935 element == EL_DC_MAGIC_WALL_ACTIVE ||
12936 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12937 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12946 debug_print_timestamp(0, "- time for MAIN loop: -->");
12949 #if USE_NEW_AMOEBA_CODE
12950 /* new experimental amoeba growth stuff */
12951 if (!(FrameCounter % 8))
12953 static unsigned long random = 1684108901;
12955 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12957 x = RND(lev_fieldx);
12958 y = RND(lev_fieldy);
12959 element = Feld[x][y];
12961 if (!IS_PLAYER(x,y) &&
12962 (element == EL_EMPTY ||
12963 CAN_GROW_INTO(element) ||
12964 element == EL_QUICKSAND_EMPTY ||
12965 element == EL_QUICKSAND_FAST_EMPTY ||
12966 element == EL_ACID_SPLASH_LEFT ||
12967 element == EL_ACID_SPLASH_RIGHT))
12969 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12970 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12971 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12972 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12973 Feld[x][y] = EL_AMOEBA_DROP;
12976 random = random * 129 + 1;
12982 if (game.explosions_delayed)
12985 game.explosions_delayed = FALSE;
12987 SCAN_PLAYFIELD(x, y)
12989 element = Feld[x][y];
12991 if (ExplodeField[x][y])
12992 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12993 else if (element == EL_EXPLOSION)
12994 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12996 ExplodeField[x][y] = EX_TYPE_NONE;
12999 game.explosions_delayed = TRUE;
13002 if (game.magic_wall_active)
13004 if (!(game.magic_wall_time_left % 4))
13006 int element = Feld[magic_wall_x][magic_wall_y];
13008 if (element == EL_BD_MAGIC_WALL_FULL ||
13009 element == EL_BD_MAGIC_WALL_ACTIVE ||
13010 element == EL_BD_MAGIC_WALL_EMPTYING)
13011 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13012 else if (element == EL_DC_MAGIC_WALL_FULL ||
13013 element == EL_DC_MAGIC_WALL_ACTIVE ||
13014 element == EL_DC_MAGIC_WALL_EMPTYING)
13015 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13017 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13020 if (game.magic_wall_time_left > 0)
13022 game.magic_wall_time_left--;
13024 if (!game.magic_wall_time_left)
13026 SCAN_PLAYFIELD(x, y)
13028 element = Feld[x][y];
13030 if (element == EL_MAGIC_WALL_ACTIVE ||
13031 element == EL_MAGIC_WALL_FULL)
13033 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13034 TEST_DrawLevelField(x, y);
13036 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13037 element == EL_BD_MAGIC_WALL_FULL)
13039 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13040 TEST_DrawLevelField(x, y);
13042 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13043 element == EL_DC_MAGIC_WALL_FULL)
13045 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13046 TEST_DrawLevelField(x, y);
13050 game.magic_wall_active = FALSE;
13055 if (game.light_time_left > 0)
13057 game.light_time_left--;
13059 if (game.light_time_left == 0)
13060 RedrawAllLightSwitchesAndInvisibleElements();
13063 if (game.timegate_time_left > 0)
13065 game.timegate_time_left--;
13067 if (game.timegate_time_left == 0)
13068 CloseAllOpenTimegates();
13071 if (game.lenses_time_left > 0)
13073 game.lenses_time_left--;
13075 if (game.lenses_time_left == 0)
13076 RedrawAllInvisibleElementsForLenses();
13079 if (game.magnify_time_left > 0)
13081 game.magnify_time_left--;
13083 if (game.magnify_time_left == 0)
13084 RedrawAllInvisibleElementsForMagnifier();
13087 for (i = 0; i < MAX_PLAYERS; i++)
13089 struct PlayerInfo *player = &stored_player[i];
13091 if (SHIELD_ON(player))
13093 if (player->shield_deadly_time_left)
13094 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13095 else if (player->shield_normal_time_left)
13096 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13100 #if USE_DELAYED_GFX_REDRAW
13101 SCAN_PLAYFIELD(x, y)
13104 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13106 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13107 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13110 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13111 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13113 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13114 DrawLevelField(x, y);
13116 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13117 DrawLevelFieldCrumbledSand(x, y);
13119 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13120 DrawLevelFieldCrumbledSandNeighbours(x, y);
13122 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13123 DrawTwinkleOnField(x, y);
13126 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13133 PlayAllPlayersSound();
13135 if (options.debug) /* calculate frames per second */
13137 static unsigned long fps_counter = 0;
13138 static int fps_frames = 0;
13139 unsigned long fps_delay_ms = Counter() - fps_counter;
13143 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13145 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13148 fps_counter = Counter();
13151 redraw_mask |= REDRAW_FPS;
13154 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13156 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13158 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13160 local_player->show_envelope = 0;
13164 debug_print_timestamp(0, "stop main loop profiling ");
13165 printf("----------------------------------------------------------\n");
13168 /* use random number generator in every frame to make it less predictable */
13169 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13173 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13175 int min_x = x, min_y = y, max_x = x, max_y = y;
13178 for (i = 0; i < MAX_PLAYERS; i++)
13180 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13182 if (!stored_player[i].active || &stored_player[i] == player)
13185 min_x = MIN(min_x, jx);
13186 min_y = MIN(min_y, jy);
13187 max_x = MAX(max_x, jx);
13188 max_y = MAX(max_y, jy);
13191 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13194 static boolean AllPlayersInVisibleScreen()
13198 for (i = 0; i < MAX_PLAYERS; i++)
13200 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13202 if (!stored_player[i].active)
13205 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13212 void ScrollLevel(int dx, int dy)
13215 /* (directly solved in BlitBitmap() now) */
13216 static Bitmap *bitmap_db_field2 = NULL;
13217 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13224 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13225 /* only horizontal XOR vertical scroll direction allowed */
13226 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13231 /* (directly solved in BlitBitmap() now) */
13232 if (bitmap_db_field2 == NULL)
13233 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13235 /* needed when blitting directly to same bitmap -- should not be needed with
13236 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13237 BlitBitmap(drawto_field, bitmap_db_field2,
13238 FX + TILEX * (dx == -1) - softscroll_offset,
13239 FY + TILEY * (dy == -1) - softscroll_offset,
13240 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13241 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13242 FX + TILEX * (dx == 1) - softscroll_offset,
13243 FY + TILEY * (dy == 1) - softscroll_offset);
13244 BlitBitmap(bitmap_db_field2, drawto_field,
13245 FX + TILEX * (dx == 1) - softscroll_offset,
13246 FY + TILEY * (dy == 1) - softscroll_offset,
13247 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13248 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13249 FX + TILEX * (dx == 1) - softscroll_offset,
13250 FY + TILEY * (dy == 1) - softscroll_offset);
13255 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13256 int xsize = (BX2 - BX1 + 1);
13257 int ysize = (BY2 - BY1 + 1);
13258 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13259 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13260 int step = (start < end ? +1 : -1);
13262 for (i = start; i != end; i += step)
13264 BlitBitmap(drawto_field, drawto_field,
13265 FX + TILEX * (dx != 0 ? i + step : 0),
13266 FY + TILEY * (dy != 0 ? i + step : 0),
13267 TILEX * (dx != 0 ? 1 : xsize),
13268 TILEY * (dy != 0 ? 1 : ysize),
13269 FX + TILEX * (dx != 0 ? i : 0),
13270 FY + TILEY * (dy != 0 ? i : 0));
13275 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13277 BlitBitmap(drawto_field, drawto_field,
13278 FX + TILEX * (dx == -1) - softscroll_offset,
13279 FY + TILEY * (dy == -1) - softscroll_offset,
13280 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13281 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13282 FX + TILEX * (dx == 1) - softscroll_offset,
13283 FY + TILEY * (dy == 1) - softscroll_offset);
13289 x = (dx == 1 ? BX1 : BX2);
13290 for (y = BY1; y <= BY2; y++)
13291 DrawScreenField(x, y);
13296 y = (dy == 1 ? BY1 : BY2);
13297 for (x = BX1; x <= BX2; x++)
13298 DrawScreenField(x, y);
13301 redraw_mask |= REDRAW_FIELD;
13304 static boolean canFallDown(struct PlayerInfo *player)
13306 int jx = player->jx, jy = player->jy;
13308 return (IN_LEV_FIELD(jx, jy + 1) &&
13309 (IS_FREE(jx, jy + 1) ||
13310 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13311 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13312 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13315 static boolean canPassField(int x, int y, int move_dir)
13317 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13318 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13319 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13320 int nextx = x + dx;
13321 int nexty = y + dy;
13322 int element = Feld[x][y];
13324 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13325 !CAN_MOVE(element) &&
13326 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13327 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13328 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13331 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13333 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13334 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13335 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13339 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13340 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13341 (IS_DIGGABLE(Feld[newx][newy]) ||
13342 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13343 canPassField(newx, newy, move_dir)));
13346 static void CheckGravityMovement(struct PlayerInfo *player)
13348 #if USE_PLAYER_GRAVITY
13349 if (player->gravity && !player->programmed_action)
13351 if (game.gravity && !player->programmed_action)
13354 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13355 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13356 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13357 int jx = player->jx, jy = player->jy;
13358 boolean player_is_moving_to_valid_field =
13359 (!player_is_snapping &&
13360 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13361 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13362 boolean player_can_fall_down = canFallDown(player);
13364 if (player_can_fall_down &&
13365 !player_is_moving_to_valid_field)
13366 player->programmed_action = MV_DOWN;
13370 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13372 return CheckGravityMovement(player);
13374 #if USE_PLAYER_GRAVITY
13375 if (player->gravity && !player->programmed_action)
13377 if (game.gravity && !player->programmed_action)
13380 int jx = player->jx, jy = player->jy;
13381 boolean field_under_player_is_free =
13382 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13383 boolean player_is_standing_on_valid_field =
13384 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13385 (IS_WALKABLE(Feld[jx][jy]) &&
13386 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13388 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13389 player->programmed_action = MV_DOWN;
13394 MovePlayerOneStep()
13395 -----------------------------------------------------------------------------
13396 dx, dy: direction (non-diagonal) to try to move the player to
13397 real_dx, real_dy: direction as read from input device (can be diagonal)
13400 boolean MovePlayerOneStep(struct PlayerInfo *player,
13401 int dx, int dy, int real_dx, int real_dy)
13403 int jx = player->jx, jy = player->jy;
13404 int new_jx = jx + dx, new_jy = jy + dy;
13405 #if !USE_FIXED_DONT_RUN_INTO
13409 boolean player_can_move = !player->cannot_move;
13411 if (!player->active || (!dx && !dy))
13412 return MP_NO_ACTION;
13414 player->MovDir = (dx < 0 ? MV_LEFT :
13415 dx > 0 ? MV_RIGHT :
13417 dy > 0 ? MV_DOWN : MV_NONE);
13419 if (!IN_LEV_FIELD(new_jx, new_jy))
13420 return MP_NO_ACTION;
13422 if (!player_can_move)
13424 if (player->MovPos == 0)
13426 player->is_moving = FALSE;
13427 player->is_digging = FALSE;
13428 player->is_collecting = FALSE;
13429 player->is_snapping = FALSE;
13430 player->is_pushing = FALSE;
13435 if (!options.network && game.centered_player_nr == -1 &&
13436 !AllPlayersInSight(player, new_jx, new_jy))
13437 return MP_NO_ACTION;
13439 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13440 return MP_NO_ACTION;
13443 #if !USE_FIXED_DONT_RUN_INTO
13444 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13446 /* (moved to DigField()) */
13447 if (player_can_move && DONT_RUN_INTO(element))
13449 if (element == EL_ACID && dx == 0 && dy == 1)
13451 SplashAcid(new_jx, new_jy);
13452 Feld[jx][jy] = EL_PLAYER_1;
13453 InitMovingField(jx, jy, MV_DOWN);
13454 Store[jx][jy] = EL_ACID;
13455 ContinueMoving(jx, jy);
13456 BuryPlayer(player);
13459 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13465 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13466 if (can_move != MP_MOVING)
13469 /* check if DigField() has caused relocation of the player */
13470 if (player->jx != jx || player->jy != jy)
13471 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13473 StorePlayer[jx][jy] = 0;
13474 player->last_jx = jx;
13475 player->last_jy = jy;
13476 player->jx = new_jx;
13477 player->jy = new_jy;
13478 StorePlayer[new_jx][new_jy] = player->element_nr;
13480 if (player->move_delay_value_next != -1)
13482 player->move_delay_value = player->move_delay_value_next;
13483 player->move_delay_value_next = -1;
13487 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13489 player->step_counter++;
13491 PlayerVisit[jx][jy] = FrameCounter;
13493 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13494 player->is_moving = TRUE;
13498 /* should better be called in MovePlayer(), but this breaks some tapes */
13499 ScrollPlayer(player, SCROLL_INIT);
13505 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13507 int jx = player->jx, jy = player->jy;
13508 int old_jx = jx, old_jy = jy;
13509 int moved = MP_NO_ACTION;
13511 if (!player->active)
13516 if (player->MovPos == 0)
13518 player->is_moving = FALSE;
13519 player->is_digging = FALSE;
13520 player->is_collecting = FALSE;
13521 player->is_snapping = FALSE;
13522 player->is_pushing = FALSE;
13528 if (player->move_delay > 0)
13531 player->move_delay = -1; /* set to "uninitialized" value */
13533 /* store if player is automatically moved to next field */
13534 player->is_auto_moving = (player->programmed_action != MV_NONE);
13536 /* remove the last programmed player action */
13537 player->programmed_action = 0;
13539 if (player->MovPos)
13541 /* should only happen if pre-1.2 tape recordings are played */
13542 /* this is only for backward compatibility */
13544 int original_move_delay_value = player->move_delay_value;
13547 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13551 /* scroll remaining steps with finest movement resolution */
13552 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13554 while (player->MovPos)
13556 ScrollPlayer(player, SCROLL_GO_ON);
13557 ScrollScreen(NULL, SCROLL_GO_ON);
13559 AdvanceFrameAndPlayerCounters(player->index_nr);
13565 player->move_delay_value = original_move_delay_value;
13568 player->is_active = FALSE;
13570 if (player->last_move_dir & MV_HORIZONTAL)
13572 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13573 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13577 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13578 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13581 #if USE_FIXED_BORDER_RUNNING_GFX
13582 if (!moved && !player->is_active)
13584 player->is_moving = FALSE;
13585 player->is_digging = FALSE;
13586 player->is_collecting = FALSE;
13587 player->is_snapping = FALSE;
13588 player->is_pushing = FALSE;
13596 if (moved & MP_MOVING && !ScreenMovPos &&
13597 (player->index_nr == game.centered_player_nr ||
13598 game.centered_player_nr == -1))
13600 if (moved & MP_MOVING && !ScreenMovPos &&
13601 (player == local_player || !options.network))
13604 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13605 int offset = game.scroll_delay_value;
13607 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13609 /* actual player has left the screen -- scroll in that direction */
13610 if (jx != old_jx) /* player has moved horizontally */
13611 scroll_x += (jx - old_jx);
13612 else /* player has moved vertically */
13613 scroll_y += (jy - old_jy);
13617 if (jx != old_jx) /* player has moved horizontally */
13619 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13620 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13621 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13623 /* don't scroll over playfield boundaries */
13624 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13625 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13627 /* don't scroll more than one field at a time */
13628 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13630 /* don't scroll against the player's moving direction */
13631 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13632 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13633 scroll_x = old_scroll_x;
13635 else /* player has moved vertically */
13637 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13638 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13639 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13641 /* don't scroll over playfield boundaries */
13642 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13643 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13645 /* don't scroll more than one field at a time */
13646 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13648 /* don't scroll against the player's moving direction */
13649 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13650 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13651 scroll_y = old_scroll_y;
13655 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13658 if (!options.network && game.centered_player_nr == -1 &&
13659 !AllPlayersInVisibleScreen())
13661 scroll_x = old_scroll_x;
13662 scroll_y = old_scroll_y;
13666 if (!options.network && !AllPlayersInVisibleScreen())
13668 scroll_x = old_scroll_x;
13669 scroll_y = old_scroll_y;
13674 ScrollScreen(player, SCROLL_INIT);
13675 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13680 player->StepFrame = 0;
13682 if (moved & MP_MOVING)
13684 if (old_jx != jx && old_jy == jy)
13685 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13686 else if (old_jx == jx && old_jy != jy)
13687 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13689 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13691 player->last_move_dir = player->MovDir;
13692 player->is_moving = TRUE;
13693 player->is_snapping = FALSE;
13694 player->is_switching = FALSE;
13695 player->is_dropping = FALSE;
13696 player->is_dropping_pressed = FALSE;
13697 player->drop_pressed_delay = 0;
13700 /* should better be called here than above, but this breaks some tapes */
13701 ScrollPlayer(player, SCROLL_INIT);
13706 CheckGravityMovementWhenNotMoving(player);
13708 player->is_moving = FALSE;
13710 /* at this point, the player is allowed to move, but cannot move right now
13711 (e.g. because of something blocking the way) -- ensure that the player
13712 is also allowed to move in the next frame (in old versions before 3.1.1,
13713 the player was forced to wait again for eight frames before next try) */
13715 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13716 player->move_delay = 0; /* allow direct movement in the next frame */
13719 if (player->move_delay == -1) /* not yet initialized by DigField() */
13720 player->move_delay = player->move_delay_value;
13722 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13724 TestIfPlayerTouchesBadThing(jx, jy);
13725 TestIfPlayerTouchesCustomElement(jx, jy);
13728 if (!player->active)
13729 RemovePlayer(player);
13734 void ScrollPlayer(struct PlayerInfo *player, int mode)
13736 int jx = player->jx, jy = player->jy;
13737 int last_jx = player->last_jx, last_jy = player->last_jy;
13738 int move_stepsize = TILEX / player->move_delay_value;
13740 #if USE_NEW_PLAYER_SPEED
13741 if (!player->active)
13744 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13747 if (!player->active || player->MovPos == 0)
13751 if (mode == SCROLL_INIT)
13753 player->actual_frame_counter = FrameCounter;
13754 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13756 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13757 Feld[last_jx][last_jy] == EL_EMPTY)
13759 int last_field_block_delay = 0; /* start with no blocking at all */
13760 int block_delay_adjustment = player->block_delay_adjustment;
13762 /* if player blocks last field, add delay for exactly one move */
13763 if (player->block_last_field)
13765 last_field_block_delay += player->move_delay_value;
13767 /* when blocking enabled, prevent moving up despite gravity */
13768 #if USE_PLAYER_GRAVITY
13769 if (player->gravity && player->MovDir == MV_UP)
13770 block_delay_adjustment = -1;
13772 if (game.gravity && player->MovDir == MV_UP)
13773 block_delay_adjustment = -1;
13777 /* add block delay adjustment (also possible when not blocking) */
13778 last_field_block_delay += block_delay_adjustment;
13780 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13781 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13784 #if USE_NEW_PLAYER_SPEED
13785 if (player->MovPos != 0) /* player has not yet reached destination */
13791 else if (!FrameReached(&player->actual_frame_counter, 1))
13794 #if USE_NEW_PLAYER_SPEED
13795 if (player->MovPos != 0)
13797 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13798 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13800 /* before DrawPlayer() to draw correct player graphic for this case */
13801 if (player->MovPos == 0)
13802 CheckGravityMovement(player);
13805 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13806 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13808 /* before DrawPlayer() to draw correct player graphic for this case */
13809 if (player->MovPos == 0)
13810 CheckGravityMovement(player);
13813 if (player->MovPos == 0) /* player reached destination field */
13815 if (player->move_delay_reset_counter > 0)
13817 player->move_delay_reset_counter--;
13819 if (player->move_delay_reset_counter == 0)
13821 /* continue with normal speed after quickly moving through gate */
13822 HALVE_PLAYER_SPEED(player);
13824 /* be able to make the next move without delay */
13825 player->move_delay = 0;
13829 player->last_jx = jx;
13830 player->last_jy = jy;
13832 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13833 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13835 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13837 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13838 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13840 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13842 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13843 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13845 DrawPlayer(player); /* needed here only to cleanup last field */
13846 RemovePlayer(player);
13848 if (local_player->friends_still_needed == 0 ||
13849 IS_SP_ELEMENT(Feld[jx][jy]))
13850 PlayerWins(player);
13853 /* this breaks one level: "machine", level 000 */
13855 int move_direction = player->MovDir;
13856 int enter_side = MV_DIR_OPPOSITE(move_direction);
13857 int leave_side = move_direction;
13858 int old_jx = last_jx;
13859 int old_jy = last_jy;
13860 int old_element = Feld[old_jx][old_jy];
13861 int new_element = Feld[jx][jy];
13863 if (IS_CUSTOM_ELEMENT(old_element))
13864 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13866 player->index_bit, leave_side);
13868 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13869 CE_PLAYER_LEAVES_X,
13870 player->index_bit, leave_side);
13872 if (IS_CUSTOM_ELEMENT(new_element))
13873 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13874 player->index_bit, enter_side);
13876 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13877 CE_PLAYER_ENTERS_X,
13878 player->index_bit, enter_side);
13880 #if USE_FIX_CE_ACTION_WITH_PLAYER
13881 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13882 CE_MOVE_OF_X, move_direction);
13884 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13885 CE_MOVE_OF_X, move_direction);
13889 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13891 TestIfPlayerTouchesBadThing(jx, jy);
13892 TestIfPlayerTouchesCustomElement(jx, jy);
13894 /* needed because pushed element has not yet reached its destination,
13895 so it would trigger a change event at its previous field location */
13896 if (!player->is_pushing)
13897 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13899 if (!player->active)
13900 RemovePlayer(player);
13903 if (!local_player->LevelSolved && level.use_step_counter)
13913 if (TimeLeft <= 10 && setup.time_limit)
13914 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13917 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13919 DisplayGameControlValues();
13921 DrawGameValue_Time(TimeLeft);
13924 if (!TimeLeft && setup.time_limit)
13925 for (i = 0; i < MAX_PLAYERS; i++)
13926 KillPlayer(&stored_player[i]);
13929 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13931 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13933 DisplayGameControlValues();
13936 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13937 DrawGameValue_Time(TimePlayed);
13941 if (tape.single_step && tape.recording && !tape.pausing &&
13942 !player->programmed_action)
13943 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13947 void ScrollScreen(struct PlayerInfo *player, int mode)
13949 static unsigned long screen_frame_counter = 0;
13951 if (mode == SCROLL_INIT)
13953 /* set scrolling step size according to actual player's moving speed */
13954 ScrollStepSize = TILEX / player->move_delay_value;
13956 screen_frame_counter = FrameCounter;
13957 ScreenMovDir = player->MovDir;
13958 ScreenMovPos = player->MovPos;
13959 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13962 else if (!FrameReached(&screen_frame_counter, 1))
13967 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13968 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13969 redraw_mask |= REDRAW_FIELD;
13972 ScreenMovDir = MV_NONE;
13975 void TestIfPlayerTouchesCustomElement(int x, int y)
13977 static int xy[4][2] =
13984 static int trigger_sides[4][2] =
13986 /* center side border side */
13987 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13988 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13989 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13990 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13992 static int touch_dir[4] =
13994 MV_LEFT | MV_RIGHT,
13999 int center_element = Feld[x][y]; /* should always be non-moving! */
14002 for (i = 0; i < NUM_DIRECTIONS; i++)
14004 int xx = x + xy[i][0];
14005 int yy = y + xy[i][1];
14006 int center_side = trigger_sides[i][0];
14007 int border_side = trigger_sides[i][1];
14008 int border_element;
14010 if (!IN_LEV_FIELD(xx, yy))
14013 if (IS_PLAYER(x, y)) /* player found at center element */
14015 struct PlayerInfo *player = PLAYERINFO(x, y);
14017 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14018 border_element = Feld[xx][yy]; /* may be moving! */
14019 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14020 border_element = Feld[xx][yy];
14021 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14022 border_element = MovingOrBlocked2Element(xx, yy);
14024 continue; /* center and border element do not touch */
14026 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14027 player->index_bit, border_side);
14028 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14029 CE_PLAYER_TOUCHES_X,
14030 player->index_bit, border_side);
14032 #if USE_FIX_CE_ACTION_WITH_PLAYER
14034 /* use player element that is initially defined in the level playfield,
14035 not the player element that corresponds to the runtime player number
14036 (example: a level that contains EL_PLAYER_3 as the only player would
14037 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14038 int player_element = PLAYERINFO(x, y)->initial_element;
14040 CheckElementChangeBySide(xx, yy, border_element, player_element,
14041 CE_TOUCHING_X, border_side);
14045 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14047 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14049 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14051 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14052 continue; /* center and border element do not touch */
14055 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14056 player->index_bit, center_side);
14057 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14058 CE_PLAYER_TOUCHES_X,
14059 player->index_bit, center_side);
14061 #if USE_FIX_CE_ACTION_WITH_PLAYER
14063 /* use player element that is initially defined in the level playfield,
14064 not the player element that corresponds to the runtime player number
14065 (example: a level that contains EL_PLAYER_3 as the only player would
14066 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14067 int player_element = PLAYERINFO(xx, yy)->initial_element;
14069 CheckElementChangeBySide(x, y, center_element, player_element,
14070 CE_TOUCHING_X, center_side);
14079 #if USE_ELEMENT_TOUCHING_BUGFIX
14081 void TestIfElementTouchesCustomElement(int x, int y)
14083 static int xy[4][2] =
14090 static int trigger_sides[4][2] =
14092 /* center side border side */
14093 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14094 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14095 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14096 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14098 static int touch_dir[4] =
14100 MV_LEFT | MV_RIGHT,
14105 boolean change_center_element = FALSE;
14106 int center_element = Feld[x][y]; /* should always be non-moving! */
14107 int border_element_old[NUM_DIRECTIONS];
14110 for (i = 0; i < NUM_DIRECTIONS; i++)
14112 int xx = x + xy[i][0];
14113 int yy = y + xy[i][1];
14114 int border_element;
14116 border_element_old[i] = -1;
14118 if (!IN_LEV_FIELD(xx, yy))
14121 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14122 border_element = Feld[xx][yy]; /* may be moving! */
14123 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14124 border_element = Feld[xx][yy];
14125 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14126 border_element = MovingOrBlocked2Element(xx, yy);
14128 continue; /* center and border element do not touch */
14130 border_element_old[i] = border_element;
14133 for (i = 0; i < NUM_DIRECTIONS; i++)
14135 int xx = x + xy[i][0];
14136 int yy = y + xy[i][1];
14137 int center_side = trigger_sides[i][0];
14138 int border_element = border_element_old[i];
14140 if (border_element == -1)
14143 /* check for change of border element */
14144 CheckElementChangeBySide(xx, yy, border_element, center_element,
14145 CE_TOUCHING_X, center_side);
14147 /* (center element cannot be player, so we dont have to check this here) */
14150 for (i = 0; i < NUM_DIRECTIONS; i++)
14152 int xx = x + xy[i][0];
14153 int yy = y + xy[i][1];
14154 int border_side = trigger_sides[i][1];
14155 int border_element = border_element_old[i];
14157 if (border_element == -1)
14160 /* check for change of center element (but change it only once) */
14161 if (!change_center_element)
14162 change_center_element =
14163 CheckElementChangeBySide(x, y, center_element, border_element,
14164 CE_TOUCHING_X, border_side);
14166 #if USE_FIX_CE_ACTION_WITH_PLAYER
14167 if (IS_PLAYER(xx, yy))
14169 /* use player element that is initially defined in the level playfield,
14170 not the player element that corresponds to the runtime player number
14171 (example: a level that contains EL_PLAYER_3 as the only player would
14172 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14173 int player_element = PLAYERINFO(xx, yy)->initial_element;
14175 CheckElementChangeBySide(x, y, center_element, player_element,
14176 CE_TOUCHING_X, border_side);
14184 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14186 static int xy[4][2] =
14193 static int trigger_sides[4][2] =
14195 /* center side border side */
14196 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14197 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14198 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14199 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14201 static int touch_dir[4] =
14203 MV_LEFT | MV_RIGHT,
14208 boolean change_center_element = FALSE;
14209 int center_element = Feld[x][y]; /* should always be non-moving! */
14212 for (i = 0; i < NUM_DIRECTIONS; i++)
14214 int xx = x + xy[i][0];
14215 int yy = y + xy[i][1];
14216 int center_side = trigger_sides[i][0];
14217 int border_side = trigger_sides[i][1];
14218 int border_element;
14220 if (!IN_LEV_FIELD(xx, yy))
14223 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14224 border_element = Feld[xx][yy]; /* may be moving! */
14225 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14226 border_element = Feld[xx][yy];
14227 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14228 border_element = MovingOrBlocked2Element(xx, yy);
14230 continue; /* center and border element do not touch */
14232 /* check for change of center element (but change it only once) */
14233 if (!change_center_element)
14234 change_center_element =
14235 CheckElementChangeBySide(x, y, center_element, border_element,
14236 CE_TOUCHING_X, border_side);
14238 /* check for change of border element */
14239 CheckElementChangeBySide(xx, yy, border_element, center_element,
14240 CE_TOUCHING_X, center_side);
14246 void TestIfElementHitsCustomElement(int x, int y, int direction)
14248 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14249 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14250 int hitx = x + dx, hity = y + dy;
14251 int hitting_element = Feld[x][y];
14252 int touched_element;
14254 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14257 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14258 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14260 if (IN_LEV_FIELD(hitx, hity))
14262 int opposite_direction = MV_DIR_OPPOSITE(direction);
14263 int hitting_side = direction;
14264 int touched_side = opposite_direction;
14265 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14266 MovDir[hitx][hity] != direction ||
14267 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14273 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14274 CE_HITTING_X, touched_side);
14276 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14277 CE_HIT_BY_X, hitting_side);
14279 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14280 CE_HIT_BY_SOMETHING, opposite_direction);
14282 #if USE_FIX_CE_ACTION_WITH_PLAYER
14283 if (IS_PLAYER(hitx, hity))
14285 /* use player element that is initially defined in the level playfield,
14286 not the player element that corresponds to the runtime player number
14287 (example: a level that contains EL_PLAYER_3 as the only player would
14288 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14289 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14291 CheckElementChangeBySide(x, y, hitting_element, player_element,
14292 CE_HITTING_X, touched_side);
14298 /* "hitting something" is also true when hitting the playfield border */
14299 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14300 CE_HITTING_SOMETHING, direction);
14304 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14306 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14307 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14308 int hitx = x + dx, hity = y + dy;
14309 int hitting_element = Feld[x][y];
14310 int touched_element;
14312 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14313 !IS_FREE(hitx, hity) &&
14314 (!IS_MOVING(hitx, hity) ||
14315 MovDir[hitx][hity] != direction ||
14316 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14319 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14323 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14327 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14328 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14330 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14331 EP_CAN_SMASH_EVERYTHING, direction);
14333 if (IN_LEV_FIELD(hitx, hity))
14335 int opposite_direction = MV_DIR_OPPOSITE(direction);
14336 int hitting_side = direction;
14337 int touched_side = opposite_direction;
14339 int touched_element = MovingOrBlocked2Element(hitx, hity);
14342 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14343 MovDir[hitx][hity] != direction ||
14344 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14353 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14354 CE_SMASHED_BY_SOMETHING, opposite_direction);
14356 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14357 CE_OTHER_IS_SMASHING, touched_side);
14359 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14360 CE_OTHER_GETS_SMASHED, hitting_side);
14366 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14368 int i, kill_x = -1, kill_y = -1;
14370 int bad_element = -1;
14371 static int test_xy[4][2] =
14378 static int test_dir[4] =
14386 for (i = 0; i < NUM_DIRECTIONS; i++)
14388 int test_x, test_y, test_move_dir, test_element;
14390 test_x = good_x + test_xy[i][0];
14391 test_y = good_y + test_xy[i][1];
14393 if (!IN_LEV_FIELD(test_x, test_y))
14397 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14399 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14401 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14402 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14404 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14405 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14409 bad_element = test_element;
14415 if (kill_x != -1 || kill_y != -1)
14417 if (IS_PLAYER(good_x, good_y))
14419 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14421 if (player->shield_deadly_time_left > 0 &&
14422 !IS_INDESTRUCTIBLE(bad_element))
14423 Bang(kill_x, kill_y);
14424 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14425 KillPlayer(player);
14428 Bang(good_x, good_y);
14432 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14434 int i, kill_x = -1, kill_y = -1;
14435 int bad_element = Feld[bad_x][bad_y];
14436 static int test_xy[4][2] =
14443 static int touch_dir[4] =
14445 MV_LEFT | MV_RIGHT,
14450 static int test_dir[4] =
14458 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14461 for (i = 0; i < NUM_DIRECTIONS; i++)
14463 int test_x, test_y, test_move_dir, test_element;
14465 test_x = bad_x + test_xy[i][0];
14466 test_y = bad_y + test_xy[i][1];
14468 if (!IN_LEV_FIELD(test_x, test_y))
14472 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14474 test_element = Feld[test_x][test_y];
14476 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14477 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14479 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14480 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14482 /* good thing is player or penguin that does not move away */
14483 if (IS_PLAYER(test_x, test_y))
14485 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14487 if (bad_element == EL_ROBOT && player->is_moving)
14488 continue; /* robot does not kill player if he is moving */
14490 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14492 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14493 continue; /* center and border element do not touch */
14501 else if (test_element == EL_PENGUIN)
14511 if (kill_x != -1 || kill_y != -1)
14513 if (IS_PLAYER(kill_x, kill_y))
14515 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14517 if (player->shield_deadly_time_left > 0 &&
14518 !IS_INDESTRUCTIBLE(bad_element))
14519 Bang(bad_x, bad_y);
14520 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14521 KillPlayer(player);
14524 Bang(kill_x, kill_y);
14528 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14530 int bad_element = Feld[bad_x][bad_y];
14531 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14532 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14533 int test_x = bad_x + dx, test_y = bad_y + dy;
14534 int test_move_dir, test_element;
14535 int kill_x = -1, kill_y = -1;
14537 if (!IN_LEV_FIELD(test_x, test_y))
14541 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14543 test_element = Feld[test_x][test_y];
14545 if (test_move_dir != bad_move_dir)
14547 /* good thing can be player or penguin that does not move away */
14548 if (IS_PLAYER(test_x, test_y))
14550 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14552 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14553 player as being hit when he is moving towards the bad thing, because
14554 the "get hit by" condition would be lost after the player stops) */
14555 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14556 return; /* player moves away from bad thing */
14561 else if (test_element == EL_PENGUIN)
14568 if (kill_x != -1 || kill_y != -1)
14570 if (IS_PLAYER(kill_x, kill_y))
14572 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14574 if (player->shield_deadly_time_left > 0 &&
14575 !IS_INDESTRUCTIBLE(bad_element))
14576 Bang(bad_x, bad_y);
14577 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14578 KillPlayer(player);
14581 Bang(kill_x, kill_y);
14585 void TestIfPlayerTouchesBadThing(int x, int y)
14587 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14590 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14592 TestIfGoodThingHitsBadThing(x, y, move_dir);
14595 void TestIfBadThingTouchesPlayer(int x, int y)
14597 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14600 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14602 TestIfBadThingHitsGoodThing(x, y, move_dir);
14605 void TestIfFriendTouchesBadThing(int x, int y)
14607 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14610 void TestIfBadThingTouchesFriend(int x, int y)
14612 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14615 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14617 int i, kill_x = bad_x, kill_y = bad_y;
14618 static int xy[4][2] =
14626 for (i = 0; i < NUM_DIRECTIONS; i++)
14630 x = bad_x + xy[i][0];
14631 y = bad_y + xy[i][1];
14632 if (!IN_LEV_FIELD(x, y))
14635 element = Feld[x][y];
14636 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14637 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14645 if (kill_x != bad_x || kill_y != bad_y)
14646 Bang(bad_x, bad_y);
14649 void KillPlayer(struct PlayerInfo *player)
14651 int jx = player->jx, jy = player->jy;
14653 if (!player->active)
14657 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14658 player->killed, player->active, player->reanimated);
14661 /* the following code was introduced to prevent an infinite loop when calling
14663 -> CheckTriggeredElementChangeExt()
14664 -> ExecuteCustomElementAction()
14666 -> (infinitely repeating the above sequence of function calls)
14667 which occurs when killing the player while having a CE with the setting
14668 "kill player X when explosion of <player X>"; the solution using a new
14669 field "player->killed" was chosen for backwards compatibility, although
14670 clever use of the fields "player->active" etc. would probably also work */
14672 if (player->killed)
14676 player->killed = TRUE;
14678 /* remove accessible field at the player's position */
14679 Feld[jx][jy] = EL_EMPTY;
14681 /* deactivate shield (else Bang()/Explode() would not work right) */
14682 player->shield_normal_time_left = 0;
14683 player->shield_deadly_time_left = 0;
14686 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14687 player->killed, player->active, player->reanimated);
14693 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14694 player->killed, player->active, player->reanimated);
14697 #if USE_PLAYER_REANIMATION
14699 if (player->reanimated) /* killed player may have been reanimated */
14700 player->killed = player->reanimated = FALSE;
14702 BuryPlayer(player);
14704 if (player->killed) /* player may have been reanimated */
14705 BuryPlayer(player);
14708 BuryPlayer(player);
14712 static void KillPlayerUnlessEnemyProtected(int x, int y)
14714 if (!PLAYER_ENEMY_PROTECTED(x, y))
14715 KillPlayer(PLAYERINFO(x, y));
14718 static void KillPlayerUnlessExplosionProtected(int x, int y)
14720 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14721 KillPlayer(PLAYERINFO(x, y));
14724 void BuryPlayer(struct PlayerInfo *player)
14726 int jx = player->jx, jy = player->jy;
14728 if (!player->active)
14731 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14732 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14734 player->GameOver = TRUE;
14735 RemovePlayer(player);
14738 void RemovePlayer(struct PlayerInfo *player)
14740 int jx = player->jx, jy = player->jy;
14741 int i, found = FALSE;
14743 player->present = FALSE;
14744 player->active = FALSE;
14746 if (!ExplodeField[jx][jy])
14747 StorePlayer[jx][jy] = 0;
14749 if (player->is_moving)
14750 TEST_DrawLevelField(player->last_jx, player->last_jy);
14752 for (i = 0; i < MAX_PLAYERS; i++)
14753 if (stored_player[i].active)
14757 AllPlayersGone = TRUE;
14763 #if USE_NEW_SNAP_DELAY
14764 static void setFieldForSnapping(int x, int y, int element, int direction)
14766 struct ElementInfo *ei = &element_info[element];
14767 int direction_bit = MV_DIR_TO_BIT(direction);
14768 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14769 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14770 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14772 Feld[x][y] = EL_ELEMENT_SNAPPING;
14773 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14775 ResetGfxAnimation(x, y);
14777 GfxElement[x][y] = element;
14778 GfxAction[x][y] = action;
14779 GfxDir[x][y] = direction;
14780 GfxFrame[x][y] = -1;
14785 =============================================================================
14786 checkDiagonalPushing()
14787 -----------------------------------------------------------------------------
14788 check if diagonal input device direction results in pushing of object
14789 (by checking if the alternative direction is walkable, diggable, ...)
14790 =============================================================================
14793 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14794 int x, int y, int real_dx, int real_dy)
14796 int jx, jy, dx, dy, xx, yy;
14798 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14801 /* diagonal direction: check alternative direction */
14806 xx = jx + (dx == 0 ? real_dx : 0);
14807 yy = jy + (dy == 0 ? real_dy : 0);
14809 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14813 =============================================================================
14815 -----------------------------------------------------------------------------
14816 x, y: field next to player (non-diagonal) to try to dig to
14817 real_dx, real_dy: direction as read from input device (can be diagonal)
14818 =============================================================================
14821 static int DigField(struct PlayerInfo *player,
14822 int oldx, int oldy, int x, int y,
14823 int real_dx, int real_dy, int mode)
14825 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14826 boolean player_was_pushing = player->is_pushing;
14827 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14828 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14829 int jx = oldx, jy = oldy;
14830 int dx = x - jx, dy = y - jy;
14831 int nextx = x + dx, nexty = y + dy;
14832 int move_direction = (dx == -1 ? MV_LEFT :
14833 dx == +1 ? MV_RIGHT :
14835 dy == +1 ? MV_DOWN : MV_NONE);
14836 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14837 int dig_side = MV_DIR_OPPOSITE(move_direction);
14838 int old_element = Feld[jx][jy];
14839 #if USE_FIXED_DONT_RUN_INTO
14840 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14846 if (is_player) /* function can also be called by EL_PENGUIN */
14848 if (player->MovPos == 0)
14850 player->is_digging = FALSE;
14851 player->is_collecting = FALSE;
14854 if (player->MovPos == 0) /* last pushing move finished */
14855 player->is_pushing = FALSE;
14857 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14859 player->is_switching = FALSE;
14860 player->push_delay = -1;
14862 return MP_NO_ACTION;
14866 #if !USE_FIXED_DONT_RUN_INTO
14867 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14868 return MP_NO_ACTION;
14871 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14872 old_element = Back[jx][jy];
14874 /* in case of element dropped at player position, check background */
14875 else if (Back[jx][jy] != EL_EMPTY &&
14876 game.engine_version >= VERSION_IDENT(2,2,0,0))
14877 old_element = Back[jx][jy];
14879 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14880 return MP_NO_ACTION; /* field has no opening in this direction */
14882 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14883 return MP_NO_ACTION; /* field has no opening in this direction */
14885 #if USE_FIXED_DONT_RUN_INTO
14886 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14890 Feld[jx][jy] = player->artwork_element;
14891 InitMovingField(jx, jy, MV_DOWN);
14892 Store[jx][jy] = EL_ACID;
14893 ContinueMoving(jx, jy);
14894 BuryPlayer(player);
14896 return MP_DONT_RUN_INTO;
14900 #if USE_FIXED_DONT_RUN_INTO
14901 if (player_can_move && DONT_RUN_INTO(element))
14903 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14905 return MP_DONT_RUN_INTO;
14909 #if USE_FIXED_DONT_RUN_INTO
14910 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14911 return MP_NO_ACTION;
14914 #if !USE_FIXED_DONT_RUN_INTO
14915 element = Feld[x][y];
14918 collect_count = element_info[element].collect_count_initial;
14920 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14921 return MP_NO_ACTION;
14923 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14924 player_can_move = player_can_move_or_snap;
14926 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14927 game.engine_version >= VERSION_IDENT(2,2,0,0))
14929 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14930 player->index_bit, dig_side);
14931 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14932 player->index_bit, dig_side);
14934 if (element == EL_DC_LANDMINE)
14937 if (Feld[x][y] != element) /* field changed by snapping */
14940 return MP_NO_ACTION;
14943 #if USE_PLAYER_GRAVITY
14944 if (player->gravity && is_player && !player->is_auto_moving &&
14945 canFallDown(player) && move_direction != MV_DOWN &&
14946 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14947 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14949 if (game.gravity && is_player && !player->is_auto_moving &&
14950 canFallDown(player) && move_direction != MV_DOWN &&
14951 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14952 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14955 if (player_can_move &&
14956 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14958 int sound_element = SND_ELEMENT(element);
14959 int sound_action = ACTION_WALKING;
14961 if (IS_RND_GATE(element))
14963 if (!player->key[RND_GATE_NR(element)])
14964 return MP_NO_ACTION;
14966 else if (IS_RND_GATE_GRAY(element))
14968 if (!player->key[RND_GATE_GRAY_NR(element)])
14969 return MP_NO_ACTION;
14971 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14973 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14974 return MP_NO_ACTION;
14976 else if (element == EL_EXIT_OPEN ||
14977 element == EL_EM_EXIT_OPEN ||
14979 element == EL_EM_EXIT_OPENING ||
14981 element == EL_STEEL_EXIT_OPEN ||
14982 element == EL_EM_STEEL_EXIT_OPEN ||
14984 element == EL_EM_STEEL_EXIT_OPENING ||
14986 element == EL_SP_EXIT_OPEN ||
14987 element == EL_SP_EXIT_OPENING)
14989 sound_action = ACTION_PASSING; /* player is passing exit */
14991 else if (element == EL_EMPTY)
14993 sound_action = ACTION_MOVING; /* nothing to walk on */
14996 /* play sound from background or player, whatever is available */
14997 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14998 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15000 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15002 else if (player_can_move &&
15003 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15005 if (!ACCESS_FROM(element, opposite_direction))
15006 return MP_NO_ACTION; /* field not accessible from this direction */
15008 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15009 return MP_NO_ACTION;
15011 if (IS_EM_GATE(element))
15013 if (!player->key[EM_GATE_NR(element)])
15014 return MP_NO_ACTION;
15016 else if (IS_EM_GATE_GRAY(element))
15018 if (!player->key[EM_GATE_GRAY_NR(element)])
15019 return MP_NO_ACTION;
15021 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15023 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15024 return MP_NO_ACTION;
15026 else if (IS_EMC_GATE(element))
15028 if (!player->key[EMC_GATE_NR(element)])
15029 return MP_NO_ACTION;
15031 else if (IS_EMC_GATE_GRAY(element))
15033 if (!player->key[EMC_GATE_GRAY_NR(element)])
15034 return MP_NO_ACTION;
15036 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15038 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15039 return MP_NO_ACTION;
15041 else if (element == EL_DC_GATE_WHITE ||
15042 element == EL_DC_GATE_WHITE_GRAY ||
15043 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15045 if (player->num_white_keys == 0)
15046 return MP_NO_ACTION;
15048 player->num_white_keys--;
15050 else if (IS_SP_PORT(element))
15052 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15053 element == EL_SP_GRAVITY_PORT_RIGHT ||
15054 element == EL_SP_GRAVITY_PORT_UP ||
15055 element == EL_SP_GRAVITY_PORT_DOWN)
15056 #if USE_PLAYER_GRAVITY
15057 player->gravity = !player->gravity;
15059 game.gravity = !game.gravity;
15061 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15062 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15063 element == EL_SP_GRAVITY_ON_PORT_UP ||
15064 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15065 #if USE_PLAYER_GRAVITY
15066 player->gravity = TRUE;
15068 game.gravity = TRUE;
15070 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15071 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15072 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15073 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15074 #if USE_PLAYER_GRAVITY
15075 player->gravity = FALSE;
15077 game.gravity = FALSE;
15081 /* automatically move to the next field with double speed */
15082 player->programmed_action = move_direction;
15084 if (player->move_delay_reset_counter == 0)
15086 player->move_delay_reset_counter = 2; /* two double speed steps */
15088 DOUBLE_PLAYER_SPEED(player);
15091 PlayLevelSoundAction(x, y, ACTION_PASSING);
15093 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15097 if (mode != DF_SNAP)
15099 GfxElement[x][y] = GFX_ELEMENT(element);
15100 player->is_digging = TRUE;
15103 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15105 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15106 player->index_bit, dig_side);
15108 if (mode == DF_SNAP)
15110 #if USE_NEW_SNAP_DELAY
15111 if (level.block_snap_field)
15112 setFieldForSnapping(x, y, element, move_direction);
15114 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15116 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15119 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15120 player->index_bit, dig_side);
15123 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15127 if (is_player && mode != DF_SNAP)
15129 GfxElement[x][y] = element;
15130 player->is_collecting = TRUE;
15133 if (element == EL_SPEED_PILL)
15135 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15137 else if (element == EL_EXTRA_TIME && level.time > 0)
15139 TimeLeft += level.extra_time;
15142 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15144 DisplayGameControlValues();
15146 DrawGameValue_Time(TimeLeft);
15149 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15151 player->shield_normal_time_left += level.shield_normal_time;
15152 if (element == EL_SHIELD_DEADLY)
15153 player->shield_deadly_time_left += level.shield_deadly_time;
15155 else if (element == EL_DYNAMITE ||
15156 element == EL_EM_DYNAMITE ||
15157 element == EL_SP_DISK_RED)
15159 if (player->inventory_size < MAX_INVENTORY_SIZE)
15160 player->inventory_element[player->inventory_size++] = element;
15162 DrawGameDoorValues();
15164 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15166 player->dynabomb_count++;
15167 player->dynabombs_left++;
15169 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15171 player->dynabomb_size++;
15173 else if (element == EL_DYNABOMB_INCREASE_POWER)
15175 player->dynabomb_xl = TRUE;
15177 else if (IS_KEY(element))
15179 player->key[KEY_NR(element)] = TRUE;
15181 DrawGameDoorValues();
15183 else if (element == EL_DC_KEY_WHITE)
15185 player->num_white_keys++;
15187 /* display white keys? */
15188 /* DrawGameDoorValues(); */
15190 else if (IS_ENVELOPE(element))
15192 player->show_envelope = element;
15194 else if (element == EL_EMC_LENSES)
15196 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15198 RedrawAllInvisibleElementsForLenses();
15200 else if (element == EL_EMC_MAGNIFIER)
15202 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15204 RedrawAllInvisibleElementsForMagnifier();
15206 else if (IS_DROPPABLE(element) ||
15207 IS_THROWABLE(element)) /* can be collected and dropped */
15211 if (collect_count == 0)
15212 player->inventory_infinite_element = element;
15214 for (i = 0; i < collect_count; i++)
15215 if (player->inventory_size < MAX_INVENTORY_SIZE)
15216 player->inventory_element[player->inventory_size++] = element;
15218 DrawGameDoorValues();
15220 else if (collect_count > 0)
15222 local_player->gems_still_needed -= collect_count;
15223 if (local_player->gems_still_needed < 0)
15224 local_player->gems_still_needed = 0;
15227 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15229 DisplayGameControlValues();
15231 DrawGameValue_Emeralds(local_player->gems_still_needed);
15235 RaiseScoreElement(element);
15236 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15239 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15240 player->index_bit, dig_side);
15242 if (mode == DF_SNAP)
15244 #if USE_NEW_SNAP_DELAY
15245 if (level.block_snap_field)
15246 setFieldForSnapping(x, y, element, move_direction);
15248 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15250 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15253 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15254 player->index_bit, dig_side);
15257 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15259 if (mode == DF_SNAP && element != EL_BD_ROCK)
15260 return MP_NO_ACTION;
15262 if (CAN_FALL(element) && dy)
15263 return MP_NO_ACTION;
15265 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15266 !(element == EL_SPRING && level.use_spring_bug))
15267 return MP_NO_ACTION;
15269 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15270 ((move_direction & MV_VERTICAL &&
15271 ((element_info[element].move_pattern & MV_LEFT &&
15272 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15273 (element_info[element].move_pattern & MV_RIGHT &&
15274 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15275 (move_direction & MV_HORIZONTAL &&
15276 ((element_info[element].move_pattern & MV_UP &&
15277 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15278 (element_info[element].move_pattern & MV_DOWN &&
15279 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15280 return MP_NO_ACTION;
15282 /* do not push elements already moving away faster than player */
15283 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15284 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15285 return MP_NO_ACTION;
15287 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15289 if (player->push_delay_value == -1 || !player_was_pushing)
15290 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15292 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15294 if (player->push_delay_value == -1)
15295 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15297 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15299 if (!player->is_pushing)
15300 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15303 player->is_pushing = TRUE;
15304 player->is_active = TRUE;
15306 if (!(IN_LEV_FIELD(nextx, nexty) &&
15307 (IS_FREE(nextx, nexty) ||
15308 (IS_SB_ELEMENT(element) &&
15309 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15310 (IS_CUSTOM_ELEMENT(element) &&
15311 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15312 return MP_NO_ACTION;
15314 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15315 return MP_NO_ACTION;
15317 if (player->push_delay == -1) /* new pushing; restart delay */
15318 player->push_delay = 0;
15320 if (player->push_delay < player->push_delay_value &&
15321 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15322 element != EL_SPRING && element != EL_BALLOON)
15324 /* make sure that there is no move delay before next try to push */
15325 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15326 player->move_delay = 0;
15328 return MP_NO_ACTION;
15331 if (IS_CUSTOM_ELEMENT(element) &&
15332 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15334 if (!DigFieldByCE(nextx, nexty, element))
15335 return MP_NO_ACTION;
15338 if (IS_SB_ELEMENT(element))
15340 if (element == EL_SOKOBAN_FIELD_FULL)
15342 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15343 local_player->sokobanfields_still_needed++;
15346 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15348 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15349 local_player->sokobanfields_still_needed--;
15352 Feld[x][y] = EL_SOKOBAN_OBJECT;
15354 if (Back[x][y] == Back[nextx][nexty])
15355 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15356 else if (Back[x][y] != 0)
15357 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15360 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15363 if (local_player->sokobanfields_still_needed == 0 &&
15364 game.emulation == EMU_SOKOBAN)
15366 PlayerWins(player);
15368 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15372 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15374 InitMovingField(x, y, move_direction);
15375 GfxAction[x][y] = ACTION_PUSHING;
15377 if (mode == DF_SNAP)
15378 ContinueMoving(x, y);
15380 MovPos[x][y] = (dx != 0 ? dx : dy);
15382 Pushed[x][y] = TRUE;
15383 Pushed[nextx][nexty] = TRUE;
15385 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15386 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15388 player->push_delay_value = -1; /* get new value later */
15390 /* check for element change _after_ element has been pushed */
15391 if (game.use_change_when_pushing_bug)
15393 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15394 player->index_bit, dig_side);
15395 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15396 player->index_bit, dig_side);
15399 else if (IS_SWITCHABLE(element))
15401 if (PLAYER_SWITCHING(player, x, y))
15403 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15404 player->index_bit, dig_side);
15409 player->is_switching = TRUE;
15410 player->switch_x = x;
15411 player->switch_y = y;
15413 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15415 if (element == EL_ROBOT_WHEEL)
15417 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15421 game.robot_wheel_active = TRUE;
15423 TEST_DrawLevelField(x, y);
15425 else if (element == EL_SP_TERMINAL)
15429 SCAN_PLAYFIELD(xx, yy)
15431 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15433 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15434 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15437 else if (IS_BELT_SWITCH(element))
15439 ToggleBeltSwitch(x, y);
15441 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15442 element == EL_SWITCHGATE_SWITCH_DOWN ||
15443 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15444 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15446 ToggleSwitchgateSwitch(x, y);
15448 else if (element == EL_LIGHT_SWITCH ||
15449 element == EL_LIGHT_SWITCH_ACTIVE)
15451 ToggleLightSwitch(x, y);
15453 else if (element == EL_TIMEGATE_SWITCH ||
15454 element == EL_DC_TIMEGATE_SWITCH)
15456 ActivateTimegateSwitch(x, y);
15458 else if (element == EL_BALLOON_SWITCH_LEFT ||
15459 element == EL_BALLOON_SWITCH_RIGHT ||
15460 element == EL_BALLOON_SWITCH_UP ||
15461 element == EL_BALLOON_SWITCH_DOWN ||
15462 element == EL_BALLOON_SWITCH_NONE ||
15463 element == EL_BALLOON_SWITCH_ANY)
15465 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15466 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15467 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15468 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15469 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15472 else if (element == EL_LAMP)
15474 Feld[x][y] = EL_LAMP_ACTIVE;
15475 local_player->lights_still_needed--;
15477 ResetGfxAnimation(x, y);
15478 TEST_DrawLevelField(x, y);
15480 else if (element == EL_TIME_ORB_FULL)
15482 Feld[x][y] = EL_TIME_ORB_EMPTY;
15484 if (level.time > 0 || level.use_time_orb_bug)
15486 TimeLeft += level.time_orb_time;
15489 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15491 DisplayGameControlValues();
15493 DrawGameValue_Time(TimeLeft);
15497 ResetGfxAnimation(x, y);
15498 TEST_DrawLevelField(x, y);
15500 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15501 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15505 game.ball_state = !game.ball_state;
15507 SCAN_PLAYFIELD(xx, yy)
15509 int e = Feld[xx][yy];
15511 if (game.ball_state)
15513 if (e == EL_EMC_MAGIC_BALL)
15514 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15515 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15516 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15520 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15521 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15522 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15523 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15528 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15529 player->index_bit, dig_side);
15531 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15532 player->index_bit, dig_side);
15534 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15535 player->index_bit, dig_side);
15541 if (!PLAYER_SWITCHING(player, x, y))
15543 player->is_switching = TRUE;
15544 player->switch_x = x;
15545 player->switch_y = y;
15547 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15548 player->index_bit, dig_side);
15549 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15550 player->index_bit, dig_side);
15552 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15553 player->index_bit, dig_side);
15554 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15555 player->index_bit, dig_side);
15558 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15559 player->index_bit, dig_side);
15560 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15561 player->index_bit, dig_side);
15563 return MP_NO_ACTION;
15566 player->push_delay = -1;
15568 if (is_player) /* function can also be called by EL_PENGUIN */
15570 if (Feld[x][y] != element) /* really digged/collected something */
15572 player->is_collecting = !player->is_digging;
15573 player->is_active = TRUE;
15580 static boolean DigFieldByCE(int x, int y, int digging_element)
15582 int element = Feld[x][y];
15584 if (!IS_FREE(x, y))
15586 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15587 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15590 /* no element can dig solid indestructible elements */
15591 if (IS_INDESTRUCTIBLE(element) &&
15592 !IS_DIGGABLE(element) &&
15593 !IS_COLLECTIBLE(element))
15596 if (AmoebaNr[x][y] &&
15597 (element == EL_AMOEBA_FULL ||
15598 element == EL_BD_AMOEBA ||
15599 element == EL_AMOEBA_GROWING))
15601 AmoebaCnt[AmoebaNr[x][y]]--;
15602 AmoebaCnt2[AmoebaNr[x][y]]--;
15605 if (IS_MOVING(x, y))
15606 RemoveMovingField(x, y);
15610 TEST_DrawLevelField(x, y);
15613 /* if digged element was about to explode, prevent the explosion */
15614 ExplodeField[x][y] = EX_TYPE_NONE;
15616 PlayLevelSoundAction(x, y, action);
15619 Store[x][y] = EL_EMPTY;
15622 /* this makes it possible to leave the removed element again */
15623 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15624 Store[x][y] = element;
15626 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15628 int move_leave_element = element_info[digging_element].move_leave_element;
15630 /* this makes it possible to leave the removed element again */
15631 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15632 element : move_leave_element);
15639 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15641 int jx = player->jx, jy = player->jy;
15642 int x = jx + dx, y = jy + dy;
15643 int snap_direction = (dx == -1 ? MV_LEFT :
15644 dx == +1 ? MV_RIGHT :
15646 dy == +1 ? MV_DOWN : MV_NONE);
15647 boolean can_continue_snapping = (level.continuous_snapping &&
15648 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15650 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15653 if (!player->active || !IN_LEV_FIELD(x, y))
15661 if (player->MovPos == 0)
15662 player->is_pushing = FALSE;
15664 player->is_snapping = FALSE;
15666 if (player->MovPos == 0)
15668 player->is_moving = FALSE;
15669 player->is_digging = FALSE;
15670 player->is_collecting = FALSE;
15676 #if USE_NEW_CONTINUOUS_SNAPPING
15677 /* prevent snapping with already pressed snap key when not allowed */
15678 if (player->is_snapping && !can_continue_snapping)
15681 if (player->is_snapping)
15685 player->MovDir = snap_direction;
15687 if (player->MovPos == 0)
15689 player->is_moving = FALSE;
15690 player->is_digging = FALSE;
15691 player->is_collecting = FALSE;
15694 player->is_dropping = FALSE;
15695 player->is_dropping_pressed = FALSE;
15696 player->drop_pressed_delay = 0;
15698 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15701 player->is_snapping = TRUE;
15702 player->is_active = TRUE;
15704 if (player->MovPos == 0)
15706 player->is_moving = FALSE;
15707 player->is_digging = FALSE;
15708 player->is_collecting = FALSE;
15711 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15712 TEST_DrawLevelField(player->last_jx, player->last_jy);
15714 TEST_DrawLevelField(x, y);
15719 static boolean DropElement(struct PlayerInfo *player)
15721 int old_element, new_element;
15722 int dropx = player->jx, dropy = player->jy;
15723 int drop_direction = player->MovDir;
15724 int drop_side = drop_direction;
15726 int drop_element = get_next_dropped_element(player);
15728 int drop_element = (player->inventory_size > 0 ?
15729 player->inventory_element[player->inventory_size - 1] :
15730 player->inventory_infinite_element != EL_UNDEFINED ?
15731 player->inventory_infinite_element :
15732 player->dynabombs_left > 0 ?
15733 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15737 player->is_dropping_pressed = TRUE;
15739 /* do not drop an element on top of another element; when holding drop key
15740 pressed without moving, dropped element must move away before the next
15741 element can be dropped (this is especially important if the next element
15742 is dynamite, which can be placed on background for historical reasons) */
15743 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15746 if (IS_THROWABLE(drop_element))
15748 dropx += GET_DX_FROM_DIR(drop_direction);
15749 dropy += GET_DY_FROM_DIR(drop_direction);
15751 if (!IN_LEV_FIELD(dropx, dropy))
15755 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15756 new_element = drop_element; /* default: no change when dropping */
15758 /* check if player is active, not moving and ready to drop */
15759 if (!player->active || player->MovPos || player->drop_delay > 0)
15762 /* check if player has anything that can be dropped */
15763 if (new_element == EL_UNDEFINED)
15766 /* check if drop key was pressed long enough for EM style dynamite */
15767 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15770 /* check if anything can be dropped at the current position */
15771 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15774 /* collected custom elements can only be dropped on empty fields */
15775 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15778 if (old_element != EL_EMPTY)
15779 Back[dropx][dropy] = old_element; /* store old element on this field */
15781 ResetGfxAnimation(dropx, dropy);
15782 ResetRandomAnimationValue(dropx, dropy);
15784 if (player->inventory_size > 0 ||
15785 player->inventory_infinite_element != EL_UNDEFINED)
15787 if (player->inventory_size > 0)
15789 player->inventory_size--;
15791 DrawGameDoorValues();
15793 if (new_element == EL_DYNAMITE)
15794 new_element = EL_DYNAMITE_ACTIVE;
15795 else if (new_element == EL_EM_DYNAMITE)
15796 new_element = EL_EM_DYNAMITE_ACTIVE;
15797 else if (new_element == EL_SP_DISK_RED)
15798 new_element = EL_SP_DISK_RED_ACTIVE;
15801 Feld[dropx][dropy] = new_element;
15803 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15804 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15805 el2img(Feld[dropx][dropy]), 0);
15807 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15809 /* needed if previous element just changed to "empty" in the last frame */
15810 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15812 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15813 player->index_bit, drop_side);
15814 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15816 player->index_bit, drop_side);
15818 TestIfElementTouchesCustomElement(dropx, dropy);
15820 else /* player is dropping a dyna bomb */
15822 player->dynabombs_left--;
15824 Feld[dropx][dropy] = new_element;
15826 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15827 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15828 el2img(Feld[dropx][dropy]), 0);
15830 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15833 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15834 InitField_WithBug1(dropx, dropy, FALSE);
15836 new_element = Feld[dropx][dropy]; /* element might have changed */
15838 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15839 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15841 int move_direction, nextx, nexty;
15843 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15844 MovDir[dropx][dropy] = drop_direction;
15846 move_direction = MovDir[dropx][dropy];
15847 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15848 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15850 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15852 #if USE_FIX_IMPACT_COLLISION
15853 /* do not cause impact style collision by dropping elements that can fall */
15854 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15856 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15860 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15861 player->is_dropping = TRUE;
15863 player->drop_pressed_delay = 0;
15864 player->is_dropping_pressed = FALSE;
15866 player->drop_x = dropx;
15867 player->drop_y = dropy;
15872 /* ------------------------------------------------------------------------- */
15873 /* game sound playing functions */
15874 /* ------------------------------------------------------------------------- */
15876 static int *loop_sound_frame = NULL;
15877 static int *loop_sound_volume = NULL;
15879 void InitPlayLevelSound()
15881 int num_sounds = getSoundListSize();
15883 checked_free(loop_sound_frame);
15884 checked_free(loop_sound_volume);
15886 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15887 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15890 static void PlayLevelSound(int x, int y, int nr)
15892 int sx = SCREENX(x), sy = SCREENY(y);
15893 int volume, stereo_position;
15894 int max_distance = 8;
15895 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15897 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15898 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15901 if (!IN_LEV_FIELD(x, y) ||
15902 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15903 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15906 volume = SOUND_MAX_VOLUME;
15908 if (!IN_SCR_FIELD(sx, sy))
15910 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15911 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15913 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15916 stereo_position = (SOUND_MAX_LEFT +
15917 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15918 (SCR_FIELDX + 2 * max_distance));
15920 if (IS_LOOP_SOUND(nr))
15922 /* This assures that quieter loop sounds do not overwrite louder ones,
15923 while restarting sound volume comparison with each new game frame. */
15925 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15928 loop_sound_volume[nr] = volume;
15929 loop_sound_frame[nr] = FrameCounter;
15932 PlaySoundExt(nr, volume, stereo_position, type);
15935 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15937 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15938 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15939 y < LEVELY(BY1) ? LEVELY(BY1) :
15940 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15944 static void PlayLevelSoundAction(int x, int y, int action)
15946 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15949 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15951 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15953 if (sound_effect != SND_UNDEFINED)
15954 PlayLevelSound(x, y, sound_effect);
15957 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15960 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15962 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15963 PlayLevelSound(x, y, sound_effect);
15966 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15968 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15970 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15971 PlayLevelSound(x, y, sound_effect);
15974 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15976 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15978 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15979 StopSound(sound_effect);
15982 static void PlayLevelMusic()
15984 if (levelset.music[level_nr] != MUS_UNDEFINED)
15985 PlayMusic(levelset.music[level_nr]); /* from config file */
15987 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15990 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15992 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15993 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15994 int x = xx - 1 - offset;
15995 int y = yy - 1 - offset;
16000 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16004 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16008 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16012 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16016 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16020 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16024 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16027 case SAMPLE_android_clone:
16028 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16031 case SAMPLE_android_move:
16032 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16035 case SAMPLE_spring:
16036 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16040 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16044 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16047 case SAMPLE_eater_eat:
16048 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16052 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16055 case SAMPLE_collect:
16056 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16059 case SAMPLE_diamond:
16060 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16063 case SAMPLE_squash:
16064 /* !!! CHECK THIS !!! */
16066 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16068 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16072 case SAMPLE_wonderfall:
16073 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16077 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16081 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16085 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16089 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16093 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16097 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16100 case SAMPLE_wonder:
16101 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16105 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16108 case SAMPLE_exit_open:
16109 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16112 case SAMPLE_exit_leave:
16113 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16116 case SAMPLE_dynamite:
16117 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16121 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16125 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16129 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16133 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16137 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16141 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16145 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16151 void ChangeTime(int value)
16153 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16157 /* EMC game engine uses value from time counter of RND game engine */
16158 level.native_em_level->lev->time = *time;
16160 DrawGameValue_Time(*time);
16163 void RaiseScore(int value)
16165 /* EMC game engine and RND game engine have separate score counters */
16166 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16167 &level.native_em_level->lev->score : &local_player->score);
16171 DrawGameValue_Score(*score);
16175 void RaiseScore(int value)
16177 local_player->score += value;
16180 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16182 DisplayGameControlValues();
16184 DrawGameValue_Score(local_player->score);
16188 void RaiseScoreElement(int element)
16193 case EL_BD_DIAMOND:
16194 case EL_EMERALD_YELLOW:
16195 case EL_EMERALD_RED:
16196 case EL_EMERALD_PURPLE:
16197 case EL_SP_INFOTRON:
16198 RaiseScore(level.score[SC_EMERALD]);
16201 RaiseScore(level.score[SC_DIAMOND]);
16204 RaiseScore(level.score[SC_CRYSTAL]);
16207 RaiseScore(level.score[SC_PEARL]);
16210 case EL_BD_BUTTERFLY:
16211 case EL_SP_ELECTRON:
16212 RaiseScore(level.score[SC_BUG]);
16215 case EL_BD_FIREFLY:
16216 case EL_SP_SNIKSNAK:
16217 RaiseScore(level.score[SC_SPACESHIP]);
16220 case EL_DARK_YAMYAM:
16221 RaiseScore(level.score[SC_YAMYAM]);
16224 RaiseScore(level.score[SC_ROBOT]);
16227 RaiseScore(level.score[SC_PACMAN]);
16230 RaiseScore(level.score[SC_NUT]);
16233 case EL_EM_DYNAMITE:
16234 case EL_SP_DISK_RED:
16235 case EL_DYNABOMB_INCREASE_NUMBER:
16236 case EL_DYNABOMB_INCREASE_SIZE:
16237 case EL_DYNABOMB_INCREASE_POWER:
16238 RaiseScore(level.score[SC_DYNAMITE]);
16240 case EL_SHIELD_NORMAL:
16241 case EL_SHIELD_DEADLY:
16242 RaiseScore(level.score[SC_SHIELD]);
16244 case EL_EXTRA_TIME:
16245 RaiseScore(level.extra_time_score);
16259 case EL_DC_KEY_WHITE:
16260 RaiseScore(level.score[SC_KEY]);
16263 RaiseScore(element_info[element].collect_score);
16268 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16270 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16272 #if defined(NETWORK_AVALIABLE)
16273 if (options.network)
16274 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16283 FadeSkipNextFadeIn();
16285 fading = fading_none;
16289 OpenDoor(DOOR_CLOSE_1);
16292 game_status = GAME_MODE_MAIN;
16295 DrawAndFadeInMainMenu(REDRAW_FIELD);
16303 FadeOut(REDRAW_FIELD);
16306 game_status = GAME_MODE_MAIN;
16308 DrawAndFadeInMainMenu(REDRAW_FIELD);
16312 else /* continue playing the game */
16314 if (tape.playing && tape.deactivate_display)
16315 TapeDeactivateDisplayOff(TRUE);
16317 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16319 if (tape.playing && tape.deactivate_display)
16320 TapeDeactivateDisplayOn();
16324 void RequestQuitGame(boolean ask_if_really_quit)
16326 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16327 boolean skip_request = AllPlayersGone || quick_quit;
16329 RequestQuitGameExt(skip_request, quick_quit,
16330 "Do you really want to quit the game ?");
16334 /* ------------------------------------------------------------------------- */
16335 /* random generator functions */
16336 /* ------------------------------------------------------------------------- */
16338 unsigned int InitEngineRandom_RND(long seed)
16340 game.num_random_calls = 0;
16343 unsigned int rnd_seed = InitEngineRandom(seed);
16345 printf("::: START RND: %d\n", rnd_seed);
16350 return InitEngineRandom(seed);
16356 unsigned int RND(int max)
16360 game.num_random_calls++;
16362 return GetEngineRandom(max);
16369 /* ------------------------------------------------------------------------- */
16370 /* game engine snapshot handling functions */
16371 /* ------------------------------------------------------------------------- */
16373 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16375 struct EngineSnapshotInfo
16377 /* runtime values for custom element collect score */
16378 int collect_score[NUM_CUSTOM_ELEMENTS];
16380 /* runtime values for group element choice position */
16381 int choice_pos[NUM_GROUP_ELEMENTS];
16383 /* runtime values for belt position animations */
16384 int belt_graphic[4 * NUM_BELT_PARTS];
16385 int belt_anim_mode[4 * NUM_BELT_PARTS];
16388 struct EngineSnapshotNodeInfo
16395 static struct EngineSnapshotInfo engine_snapshot_rnd;
16396 static ListNode *engine_snapshot_list = NULL;
16397 static char *snapshot_level_identifier = NULL;
16398 static int snapshot_level_nr = -1;
16400 void FreeEngineSnapshot()
16402 while (engine_snapshot_list != NULL)
16403 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16406 setString(&snapshot_level_identifier, NULL);
16407 snapshot_level_nr = -1;
16410 static void SaveEngineSnapshotValues_RND()
16412 static int belt_base_active_element[4] =
16414 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16415 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16416 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16417 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16421 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16423 int element = EL_CUSTOM_START + i;
16425 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16428 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16430 int element = EL_GROUP_START + i;
16432 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16435 for (i = 0; i < 4; i++)
16437 for (j = 0; j < NUM_BELT_PARTS; j++)
16439 int element = belt_base_active_element[i] + j;
16440 int graphic = el2img(element);
16441 int anim_mode = graphic_info[graphic].anim_mode;
16443 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16444 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16449 static void LoadEngineSnapshotValues_RND()
16451 unsigned long num_random_calls = game.num_random_calls;
16454 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16456 int element = EL_CUSTOM_START + i;
16458 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16461 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16463 int element = EL_GROUP_START + i;
16465 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16468 for (i = 0; i < 4; i++)
16470 for (j = 0; j < NUM_BELT_PARTS; j++)
16472 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16473 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16475 graphic_info[graphic].anim_mode = anim_mode;
16479 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16481 InitRND(tape.random_seed);
16482 for (i = 0; i < num_random_calls; i++)
16486 if (game.num_random_calls != num_random_calls)
16488 Error(ERR_INFO, "number of random calls out of sync");
16489 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16490 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16491 Error(ERR_EXIT, "this should not happen -- please debug");
16495 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16497 struct EngineSnapshotNodeInfo *bi =
16498 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16500 bi->buffer_orig = buffer;
16501 bi->buffer_copy = checked_malloc(size);
16504 memcpy(bi->buffer_copy, buffer, size);
16506 addNodeToList(&engine_snapshot_list, NULL, bi);
16509 void SaveEngineSnapshot()
16511 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16513 if (level_editor_test_game) /* do not save snapshots from editor */
16516 /* copy some special values to a structure better suited for the snapshot */
16518 SaveEngineSnapshotValues_RND();
16519 SaveEngineSnapshotValues_EM();
16521 /* save values stored in special snapshot structure */
16523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16526 /* save further RND engine values */
16528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16532 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16533 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16592 /* save level identification information */
16594 setString(&snapshot_level_identifier, leveldir_current->identifier);
16595 snapshot_level_nr = level_nr;
16598 ListNode *node = engine_snapshot_list;
16601 while (node != NULL)
16603 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16608 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16612 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16614 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16617 void LoadEngineSnapshot()
16619 ListNode *node = engine_snapshot_list;
16621 if (engine_snapshot_list == NULL)
16624 while (node != NULL)
16626 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16631 /* restore special values from snapshot structure */
16633 LoadEngineSnapshotValues_RND();
16634 LoadEngineSnapshotValues_EM();
16637 boolean CheckEngineSnapshot()
16639 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16640 snapshot_level_nr == level_nr);
16644 /* ---------- new game button stuff ---------------------------------------- */
16646 /* graphic position values for game buttons */
16647 #define GAME_BUTTON_XSIZE 30
16648 #define GAME_BUTTON_YSIZE 30
16649 #define GAME_BUTTON_XPOS 5
16650 #define GAME_BUTTON_YPOS 215
16651 #define SOUND_BUTTON_XPOS 5
16652 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16654 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16655 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16656 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16657 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16658 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16659 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16667 } gamebutton_info[NUM_GAME_BUTTONS] =
16671 &game.button.stop.x, &game.button.stop.y,
16672 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16677 &game.button.pause.x, &game.button.pause.y,
16678 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16679 GAME_CTRL_ID_PAUSE,
16683 &game.button.play.x, &game.button.play.y,
16684 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16689 &game.button.sound_music.x, &game.button.sound_music.y,
16690 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16691 SOUND_CTRL_ID_MUSIC,
16692 "background music on/off"
16695 &game.button.sound_loops.x, &game.button.sound_loops.y,
16696 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16697 SOUND_CTRL_ID_LOOPS,
16698 "sound loops on/off"
16701 &game.button.sound_simple.x,&game.button.sound_simple.y,
16702 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16703 SOUND_CTRL_ID_SIMPLE,
16704 "normal sounds on/off"
16708 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16713 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16714 GAME_CTRL_ID_PAUSE,
16718 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16723 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16724 SOUND_CTRL_ID_MUSIC,
16725 "background music on/off"
16728 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16729 SOUND_CTRL_ID_LOOPS,
16730 "sound loops on/off"
16733 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16734 SOUND_CTRL_ID_SIMPLE,
16735 "normal sounds on/off"
16740 void CreateGameButtons()
16744 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16746 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16747 struct GadgetInfo *gi;
16750 unsigned long event_mask;
16752 int gd_xoffset, gd_yoffset;
16753 int gd_x1, gd_x2, gd_y1, gd_y2;
16756 x = DX + *gamebutton_info[i].x;
16757 y = DY + *gamebutton_info[i].y;
16758 gd_xoffset = gamebutton_info[i].gd_x;
16759 gd_yoffset = gamebutton_info[i].gd_y;
16760 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16761 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16763 if (id == GAME_CTRL_ID_STOP ||
16764 id == GAME_CTRL_ID_PAUSE ||
16765 id == GAME_CTRL_ID_PLAY)
16767 button_type = GD_TYPE_NORMAL_BUTTON;
16769 event_mask = GD_EVENT_RELEASED;
16770 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16771 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16775 button_type = GD_TYPE_CHECK_BUTTON;
16777 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16778 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16779 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16780 event_mask = GD_EVENT_PRESSED;
16781 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16782 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16785 gi = CreateGadget(GDI_CUSTOM_ID, id,
16786 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16791 GDI_X, DX + gd_xoffset,
16792 GDI_Y, DY + gd_yoffset,
16794 GDI_WIDTH, GAME_BUTTON_XSIZE,
16795 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16796 GDI_TYPE, button_type,
16797 GDI_STATE, GD_BUTTON_UNPRESSED,
16798 GDI_CHECKED, checked,
16799 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16800 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16801 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16802 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16803 GDI_DIRECT_DRAW, FALSE,
16804 GDI_EVENT_MASK, event_mask,
16805 GDI_CALLBACK_ACTION, HandleGameButtons,
16809 Error(ERR_EXIT, "cannot create gadget");
16811 game_gadget[id] = gi;
16815 void FreeGameButtons()
16819 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16820 FreeGadget(game_gadget[i]);
16823 static void MapGameButtons()
16827 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16828 MapGadget(game_gadget[i]);
16831 void UnmapGameButtons()
16835 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16836 UnmapGadget(game_gadget[i]);
16839 void RedrawGameButtons()
16843 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16844 RedrawGadget(game_gadget[i]);
16847 static void HandleGameButtons(struct GadgetInfo *gi)
16849 int id = gi->custom_id;
16851 if (game_status != GAME_MODE_PLAYING)
16856 case GAME_CTRL_ID_STOP:
16860 RequestQuitGame(TRUE);
16863 case GAME_CTRL_ID_PAUSE:
16864 if (options.network)
16866 #if defined(NETWORK_AVALIABLE)
16868 SendToServer_ContinuePlaying();
16870 SendToServer_PausePlaying();
16874 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16877 case GAME_CTRL_ID_PLAY:
16880 #if defined(NETWORK_AVALIABLE)
16881 if (options.network)
16882 SendToServer_ContinuePlaying();
16886 tape.pausing = FALSE;
16887 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16892 case SOUND_CTRL_ID_MUSIC:
16893 if (setup.sound_music)
16895 setup.sound_music = FALSE;
16898 else if (audio.music_available)
16900 setup.sound = setup.sound_music = TRUE;
16902 SetAudioMode(setup.sound);
16908 case SOUND_CTRL_ID_LOOPS:
16909 if (setup.sound_loops)
16910 setup.sound_loops = FALSE;
16911 else if (audio.loops_available)
16913 setup.sound = setup.sound_loops = TRUE;
16914 SetAudioMode(setup.sound);
16918 case SOUND_CTRL_ID_SIMPLE:
16919 if (setup.sound_simple)
16920 setup.sound_simple = FALSE;
16921 else if (audio.sound_available)
16923 setup.sound = setup.sound_simple = TRUE;
16924 SetAudioMode(setup.sound);