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 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12024 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12026 if (level.native_em_level->lev->home == 0) /* all players at home */
12028 PlayerWins(local_player);
12030 AllPlayersGone = TRUE;
12032 level.native_em_level->lev->home = -1;
12035 if (level.native_em_level->ply[0]->alive == 0 &&
12036 level.native_em_level->ply[1]->alive == 0 &&
12037 level.native_em_level->ply[2]->alive == 0 &&
12038 level.native_em_level->ply[3]->alive == 0) /* all dead */
12039 AllPlayersGone = TRUE;
12041 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12043 if (game_sp_info.LevelSolved &&
12044 !game_sp_info.GameOver) /* game won */
12046 PlayerWins(local_player);
12048 game_sp_info.GameOver = TRUE;
12050 AllPlayersGone = TRUE;
12053 if (game_sp_info.GameOver) /* game lost */
12054 AllPlayersGone = TRUE;
12057 if (TimeFrames >= FRAMES_PER_SECOND)
12062 for (i = 0; i < MAX_PLAYERS; i++)
12064 struct PlayerInfo *player = &stored_player[i];
12066 if (SHIELD_ON(player))
12068 player->shield_normal_time_left--;
12070 if (player->shield_deadly_time_left > 0)
12071 player->shield_deadly_time_left--;
12075 if (!local_player->LevelSolved && !level.use_step_counter)
12083 if (TimeLeft <= 10 && setup.time_limit)
12084 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12087 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12089 DisplayGameControlValues();
12091 DrawGameValue_Time(TimeLeft);
12094 if (!TimeLeft && setup.time_limit)
12096 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12097 level.native_em_level->lev->killed_out_of_time = TRUE;
12099 for (i = 0; i < MAX_PLAYERS; i++)
12100 KillPlayer(&stored_player[i]);
12104 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12106 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12108 DisplayGameControlValues();
12111 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12112 DrawGameValue_Time(TimePlayed);
12115 level.native_em_level->lev->time =
12116 (level.time == 0 ? TimePlayed : TimeLeft);
12119 if (tape.recording || tape.playing)
12120 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12124 UpdateAndDisplayGameControlValues();
12126 UpdateGameDoorValues();
12127 DrawGameDoorValues();
12131 void AdvanceFrameAndPlayerCounters(int player_nr)
12135 /* advance frame counters (global frame counter and time frame counter) */
12139 /* advance player counters (counters for move delay, move animation etc.) */
12140 for (i = 0; i < MAX_PLAYERS; i++)
12142 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12143 int move_delay_value = stored_player[i].move_delay_value;
12144 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12146 if (!advance_player_counters) /* not all players may be affected */
12149 #if USE_NEW_PLAYER_ANIM
12150 if (move_frames == 0) /* less than one move per game frame */
12152 int stepsize = TILEX / move_delay_value;
12153 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12154 int count = (stored_player[i].is_moving ?
12155 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12157 if (count % delay == 0)
12162 stored_player[i].Frame += move_frames;
12164 if (stored_player[i].MovPos != 0)
12165 stored_player[i].StepFrame += move_frames;
12167 if (stored_player[i].move_delay > 0)
12168 stored_player[i].move_delay--;
12170 /* due to bugs in previous versions, counter must count up, not down */
12171 if (stored_player[i].push_delay != -1)
12172 stored_player[i].push_delay++;
12174 if (stored_player[i].drop_delay > 0)
12175 stored_player[i].drop_delay--;
12177 if (stored_player[i].is_dropping_pressed)
12178 stored_player[i].drop_pressed_delay++;
12182 void StartGameActions(boolean init_network_game, boolean record_tape,
12185 unsigned long new_random_seed = InitRND(random_seed);
12188 TapeStartRecording(new_random_seed);
12190 #if defined(NETWORK_AVALIABLE)
12191 if (init_network_game)
12193 SendToServer_StartPlaying();
12204 static unsigned long game_frame_delay = 0;
12205 unsigned long game_frame_delay_value;
12206 byte *recorded_player_action;
12207 byte summarized_player_action = 0;
12208 byte tape_action[MAX_PLAYERS];
12211 /* detect endless loops, caused by custom element programming */
12212 if (recursion_loop_detected && recursion_loop_depth == 0)
12214 char *message = getStringCat3("Internal Error ! Element ",
12215 EL_NAME(recursion_loop_element),
12216 " caused endless loop ! Quit the game ?");
12218 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12219 EL_NAME(recursion_loop_element));
12221 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12223 recursion_loop_detected = FALSE; /* if game should be continued */
12230 if (game.restart_level)
12231 StartGameActions(options.network, setup.autorecord, level.random_seed);
12233 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12234 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12236 if (level.native_em_level->lev->home == 0) /* all players at home */
12238 PlayerWins(local_player);
12240 AllPlayersGone = TRUE;
12242 level.native_em_level->lev->home = -1;
12245 if (level.native_em_level->ply[0]->alive == 0 &&
12246 level.native_em_level->ply[1]->alive == 0 &&
12247 level.native_em_level->ply[2]->alive == 0 &&
12248 level.native_em_level->ply[3]->alive == 0) /* all dead */
12249 AllPlayersGone = TRUE;
12251 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12253 if (game_sp_info.LevelSolved &&
12254 !game_sp_info.GameOver) /* game won */
12256 PlayerWins(local_player);
12258 game_sp_info.GameOver = TRUE;
12260 AllPlayersGone = TRUE;
12263 if (game_sp_info.GameOver) /* game lost */
12264 AllPlayersGone = TRUE;
12267 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12270 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12273 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12276 game_frame_delay_value =
12277 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12279 if (tape.playing && tape.warp_forward && !tape.pausing)
12280 game_frame_delay_value = 0;
12282 /* ---------- main game synchronization point ---------- */
12284 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12286 if (network_playing && !network_player_action_received)
12288 /* try to get network player actions in time */
12290 #if defined(NETWORK_AVALIABLE)
12291 /* last chance to get network player actions without main loop delay */
12292 HandleNetworking();
12295 /* game was quit by network peer */
12296 if (game_status != GAME_MODE_PLAYING)
12299 if (!network_player_action_received)
12300 return; /* failed to get network player actions in time */
12302 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12308 /* at this point we know that we really continue executing the game */
12310 network_player_action_received = FALSE;
12312 /* when playing tape, read previously recorded player input from tape data */
12313 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12316 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12321 if (tape.set_centered_player)
12323 game.centered_player_nr_next = tape.centered_player_nr_next;
12324 game.set_centered_player = TRUE;
12327 for (i = 0; i < MAX_PLAYERS; i++)
12329 summarized_player_action |= stored_player[i].action;
12331 if (!network_playing)
12332 stored_player[i].effective_action = stored_player[i].action;
12335 #if defined(NETWORK_AVALIABLE)
12336 if (network_playing)
12337 SendToServer_MovePlayer(summarized_player_action);
12340 if (!options.network && !setup.team_mode)
12341 local_player->effective_action = summarized_player_action;
12343 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12345 for (i = 0; i < MAX_PLAYERS; i++)
12346 stored_player[i].effective_action =
12347 (i == game.centered_player_nr ? summarized_player_action : 0);
12350 if (recorded_player_action != NULL)
12351 for (i = 0; i < MAX_PLAYERS; i++)
12352 stored_player[i].effective_action = recorded_player_action[i];
12354 for (i = 0; i < MAX_PLAYERS; i++)
12356 tape_action[i] = stored_player[i].effective_action;
12358 /* (this can only happen in the R'n'D game engine) */
12359 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12360 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12363 /* only record actions from input devices, but not programmed actions */
12364 if (tape.recording)
12365 TapeRecordAction(tape_action);
12367 #if USE_NEW_PLAYER_ASSIGNMENTS
12369 byte mapped_action[MAX_PLAYERS];
12371 for (i = 0; i < MAX_PLAYERS; i++)
12372 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12374 for (i = 0; i < MAX_PLAYERS; i++)
12375 stored_player[i].effective_action = mapped_action[i];
12379 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12381 GameActions_EM_Main();
12383 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12385 GameActions_SP_Main();
12393 void GameActions_EM_Main()
12395 byte effective_action[MAX_PLAYERS];
12396 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12399 for (i = 0; i < MAX_PLAYERS; i++)
12400 effective_action[i] = stored_player[i].effective_action;
12402 GameActions_EM(effective_action, warp_mode);
12406 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12409 void GameActions_SP_Main()
12411 byte effective_action[MAX_PLAYERS];
12412 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12415 for (i = 0; i < MAX_PLAYERS; i++)
12416 effective_action[i] = stored_player[i].effective_action;
12418 GameActions_SP(effective_action, warp_mode);
12422 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12425 void GameActions_RND()
12427 int magic_wall_x = 0, magic_wall_y = 0;
12428 int i, x, y, element, graphic;
12430 InitPlayfieldScanModeVars();
12432 #if USE_ONE_MORE_CHANGE_PER_FRAME
12433 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12435 SCAN_PLAYFIELD(x, y)
12437 ChangeCount[x][y] = 0;
12438 ChangeEvent[x][y] = -1;
12443 if (game.set_centered_player)
12445 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12447 /* switching to "all players" only possible if all players fit to screen */
12448 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12450 game.centered_player_nr_next = game.centered_player_nr;
12451 game.set_centered_player = FALSE;
12454 /* do not switch focus to non-existing (or non-active) player */
12455 if (game.centered_player_nr_next >= 0 &&
12456 !stored_player[game.centered_player_nr_next].active)
12458 game.centered_player_nr_next = game.centered_player_nr;
12459 game.set_centered_player = FALSE;
12463 if (game.set_centered_player &&
12464 ScreenMovPos == 0) /* screen currently aligned at tile position */
12468 if (game.centered_player_nr_next == -1)
12470 setScreenCenteredToAllPlayers(&sx, &sy);
12474 sx = stored_player[game.centered_player_nr_next].jx;
12475 sy = stored_player[game.centered_player_nr_next].jy;
12478 game.centered_player_nr = game.centered_player_nr_next;
12479 game.set_centered_player = FALSE;
12481 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12482 DrawGameDoorValues();
12485 for (i = 0; i < MAX_PLAYERS; i++)
12487 int actual_player_action = stored_player[i].effective_action;
12490 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12491 - rnd_equinox_tetrachloride 048
12492 - rnd_equinox_tetrachloride_ii 096
12493 - rnd_emanuel_schmieg 002
12494 - doctor_sloan_ww 001, 020
12496 if (stored_player[i].MovPos == 0)
12497 CheckGravityMovement(&stored_player[i]);
12500 /* overwrite programmed action with tape action */
12501 if (stored_player[i].programmed_action)
12502 actual_player_action = stored_player[i].programmed_action;
12504 PlayerActions(&stored_player[i], actual_player_action);
12506 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12509 ScrollScreen(NULL, SCROLL_GO_ON);
12511 /* for backwards compatibility, the following code emulates a fixed bug that
12512 occured when pushing elements (causing elements that just made their last
12513 pushing step to already (if possible) make their first falling step in the
12514 same game frame, which is bad); this code is also needed to use the famous
12515 "spring push bug" which is used in older levels and might be wanted to be
12516 used also in newer levels, but in this case the buggy pushing code is only
12517 affecting the "spring" element and no other elements */
12519 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12521 for (i = 0; i < MAX_PLAYERS; i++)
12523 struct PlayerInfo *player = &stored_player[i];
12524 int x = player->jx;
12525 int y = player->jy;
12527 if (player->active && player->is_pushing && player->is_moving &&
12529 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12530 Feld[x][y] == EL_SPRING))
12532 ContinueMoving(x, y);
12534 /* continue moving after pushing (this is actually a bug) */
12535 if (!IS_MOVING(x, y))
12536 Stop[x][y] = FALSE;
12542 debug_print_timestamp(0, "start main loop profiling");
12545 SCAN_PLAYFIELD(x, y)
12547 ChangeCount[x][y] = 0;
12548 ChangeEvent[x][y] = -1;
12550 /* this must be handled before main playfield loop */
12551 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12554 if (MovDelay[x][y] <= 0)
12558 #if USE_NEW_SNAP_DELAY
12559 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12562 if (MovDelay[x][y] <= 0)
12565 TEST_DrawLevelField(x, y);
12567 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12573 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12575 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12576 printf("GameActions(): This should never happen!\n");
12578 ChangePage[x][y] = -1;
12582 Stop[x][y] = FALSE;
12583 if (WasJustMoving[x][y] > 0)
12584 WasJustMoving[x][y]--;
12585 if (WasJustFalling[x][y] > 0)
12586 WasJustFalling[x][y]--;
12587 if (CheckCollision[x][y] > 0)
12588 CheckCollision[x][y]--;
12589 if (CheckImpact[x][y] > 0)
12590 CheckImpact[x][y]--;
12594 /* reset finished pushing action (not done in ContinueMoving() to allow
12595 continuous pushing animation for elements with zero push delay) */
12596 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12598 ResetGfxAnimation(x, y);
12599 TEST_DrawLevelField(x, y);
12603 if (IS_BLOCKED(x, y))
12607 Blocked2Moving(x, y, &oldx, &oldy);
12608 if (!IS_MOVING(oldx, oldy))
12610 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12611 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12612 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12613 printf("GameActions(): This should never happen!\n");
12620 debug_print_timestamp(0, "- time for pre-main loop:");
12623 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12624 SCAN_PLAYFIELD(x, y)
12626 element = Feld[x][y];
12627 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12632 int element2 = element;
12633 int graphic2 = graphic;
12635 int element2 = Feld[x][y];
12636 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12638 int last_gfx_frame = GfxFrame[x][y];
12640 if (graphic_info[graphic2].anim_global_sync)
12641 GfxFrame[x][y] = FrameCounter;
12642 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12643 GfxFrame[x][y] = CustomValue[x][y];
12644 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12645 GfxFrame[x][y] = element_info[element2].collect_score;
12646 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12647 GfxFrame[x][y] = ChangeDelay[x][y];
12649 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12650 DrawLevelGraphicAnimation(x, y, graphic2);
12653 ResetGfxFrame(x, y, TRUE);
12657 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12658 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12659 ResetRandomAnimationValue(x, y);
12663 SetRandomAnimationValue(x, y);
12667 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12670 #endif // -------------------- !!! TEST ONLY !!! --------------------
12673 debug_print_timestamp(0, "- time for TEST loop: -->");
12676 SCAN_PLAYFIELD(x, y)
12678 element = Feld[x][y];
12679 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12681 ResetGfxFrame(x, y, TRUE);
12683 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12684 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12685 ResetRandomAnimationValue(x, y);
12687 SetRandomAnimationValue(x, y);
12689 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12691 if (IS_INACTIVE(element))
12693 if (IS_ANIMATED(graphic))
12694 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12699 /* this may take place after moving, so 'element' may have changed */
12700 if (IS_CHANGING(x, y) &&
12701 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12703 int page = element_info[element].event_page_nr[CE_DELAY];
12706 HandleElementChange(x, y, page);
12708 if (CAN_CHANGE(element))
12709 HandleElementChange(x, y, page);
12711 if (HAS_ACTION(element))
12712 ExecuteCustomElementAction(x, y, element, page);
12715 element = Feld[x][y];
12716 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12719 #if 0 // ---------------------------------------------------------------------
12721 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12725 element = Feld[x][y];
12726 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12728 if (IS_ANIMATED(graphic) &&
12729 !IS_MOVING(x, y) &&
12731 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12733 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12734 TEST_DrawTwinkleOnField(x, y);
12736 else if (IS_MOVING(x, y))
12737 ContinueMoving(x, y);
12744 case EL_EM_EXIT_OPEN:
12745 case EL_SP_EXIT_OPEN:
12746 case EL_STEEL_EXIT_OPEN:
12747 case EL_EM_STEEL_EXIT_OPEN:
12748 case EL_SP_TERMINAL:
12749 case EL_SP_TERMINAL_ACTIVE:
12750 case EL_EXTRA_TIME:
12751 case EL_SHIELD_NORMAL:
12752 case EL_SHIELD_DEADLY:
12753 if (IS_ANIMATED(graphic))
12754 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12757 case EL_DYNAMITE_ACTIVE:
12758 case EL_EM_DYNAMITE_ACTIVE:
12759 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12760 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12761 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12762 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12763 case EL_SP_DISK_RED_ACTIVE:
12764 CheckDynamite(x, y);
12767 case EL_AMOEBA_GROWING:
12768 AmoebeWaechst(x, y);
12771 case EL_AMOEBA_SHRINKING:
12772 AmoebaDisappearing(x, y);
12775 #if !USE_NEW_AMOEBA_CODE
12776 case EL_AMOEBA_WET:
12777 case EL_AMOEBA_DRY:
12778 case EL_AMOEBA_FULL:
12780 case EL_EMC_DRIPPER:
12781 AmoebeAbleger(x, y);
12785 case EL_GAME_OF_LIFE:
12790 case EL_EXIT_CLOSED:
12794 case EL_EM_EXIT_CLOSED:
12798 case EL_STEEL_EXIT_CLOSED:
12799 CheckExitSteel(x, y);
12802 case EL_EM_STEEL_EXIT_CLOSED:
12803 CheckExitSteelEM(x, y);
12806 case EL_SP_EXIT_CLOSED:
12810 case EL_EXPANDABLE_WALL_GROWING:
12811 case EL_EXPANDABLE_STEELWALL_GROWING:
12812 MauerWaechst(x, y);
12815 case EL_EXPANDABLE_WALL:
12816 case EL_EXPANDABLE_WALL_HORIZONTAL:
12817 case EL_EXPANDABLE_WALL_VERTICAL:
12818 case EL_EXPANDABLE_WALL_ANY:
12819 case EL_BD_EXPANDABLE_WALL:
12820 MauerAbleger(x, y);
12823 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12824 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12825 case EL_EXPANDABLE_STEELWALL_ANY:
12826 MauerAblegerStahl(x, y);
12830 CheckForDragon(x, y);
12836 case EL_ELEMENT_SNAPPING:
12837 case EL_DIAGONAL_SHRINKING:
12838 case EL_DIAGONAL_GROWING:
12841 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12843 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12848 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12849 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12854 #else // ---------------------------------------------------------------------
12856 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12860 element = Feld[x][y];
12861 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12863 if (IS_ANIMATED(graphic) &&
12864 !IS_MOVING(x, y) &&
12866 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12868 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12869 TEST_DrawTwinkleOnField(x, y);
12871 else if ((element == EL_ACID ||
12872 element == EL_EXIT_OPEN ||
12873 element == EL_EM_EXIT_OPEN ||
12874 element == EL_SP_EXIT_OPEN ||
12875 element == EL_STEEL_EXIT_OPEN ||
12876 element == EL_EM_STEEL_EXIT_OPEN ||
12877 element == EL_SP_TERMINAL ||
12878 element == EL_SP_TERMINAL_ACTIVE ||
12879 element == EL_EXTRA_TIME ||
12880 element == EL_SHIELD_NORMAL ||
12881 element == EL_SHIELD_DEADLY) &&
12882 IS_ANIMATED(graphic))
12883 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12884 else if (IS_MOVING(x, y))
12885 ContinueMoving(x, y);
12886 else if (IS_ACTIVE_BOMB(element))
12887 CheckDynamite(x, y);
12888 else if (element == EL_AMOEBA_GROWING)
12889 AmoebeWaechst(x, y);
12890 else if (element == EL_AMOEBA_SHRINKING)
12891 AmoebaDisappearing(x, y);
12893 #if !USE_NEW_AMOEBA_CODE
12894 else if (IS_AMOEBALIVE(element))
12895 AmoebeAbleger(x, y);
12898 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12900 else if (element == EL_EXIT_CLOSED)
12902 else if (element == EL_EM_EXIT_CLOSED)
12904 else if (element == EL_STEEL_EXIT_CLOSED)
12905 CheckExitSteel(x, y);
12906 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12907 CheckExitSteelEM(x, y);
12908 else if (element == EL_SP_EXIT_CLOSED)
12910 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12911 element == EL_EXPANDABLE_STEELWALL_GROWING)
12912 MauerWaechst(x, y);
12913 else if (element == EL_EXPANDABLE_WALL ||
12914 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12915 element == EL_EXPANDABLE_WALL_VERTICAL ||
12916 element == EL_EXPANDABLE_WALL_ANY ||
12917 element == EL_BD_EXPANDABLE_WALL)
12918 MauerAbleger(x, y);
12919 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12920 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12921 element == EL_EXPANDABLE_STEELWALL_ANY)
12922 MauerAblegerStahl(x, y);
12923 else if (element == EL_FLAMES)
12924 CheckForDragon(x, y);
12925 else if (element == EL_EXPLOSION)
12926 ; /* drawing of correct explosion animation is handled separately */
12927 else if (element == EL_ELEMENT_SNAPPING ||
12928 element == EL_DIAGONAL_SHRINKING ||
12929 element == EL_DIAGONAL_GROWING)
12931 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12933 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12935 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12936 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12938 #endif // ---------------------------------------------------------------------
12940 if (IS_BELT_ACTIVE(element))
12941 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12943 if (game.magic_wall_active)
12945 int jx = local_player->jx, jy = local_player->jy;
12947 /* play the element sound at the position nearest to the player */
12948 if ((element == EL_MAGIC_WALL_FULL ||
12949 element == EL_MAGIC_WALL_ACTIVE ||
12950 element == EL_MAGIC_WALL_EMPTYING ||
12951 element == EL_BD_MAGIC_WALL_FULL ||
12952 element == EL_BD_MAGIC_WALL_ACTIVE ||
12953 element == EL_BD_MAGIC_WALL_EMPTYING ||
12954 element == EL_DC_MAGIC_WALL_FULL ||
12955 element == EL_DC_MAGIC_WALL_ACTIVE ||
12956 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12957 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12966 debug_print_timestamp(0, "- time for MAIN loop: -->");
12969 #if USE_NEW_AMOEBA_CODE
12970 /* new experimental amoeba growth stuff */
12971 if (!(FrameCounter % 8))
12973 static unsigned long random = 1684108901;
12975 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12977 x = RND(lev_fieldx);
12978 y = RND(lev_fieldy);
12979 element = Feld[x][y];
12981 if (!IS_PLAYER(x,y) &&
12982 (element == EL_EMPTY ||
12983 CAN_GROW_INTO(element) ||
12984 element == EL_QUICKSAND_EMPTY ||
12985 element == EL_QUICKSAND_FAST_EMPTY ||
12986 element == EL_ACID_SPLASH_LEFT ||
12987 element == EL_ACID_SPLASH_RIGHT))
12989 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12990 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12991 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12992 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12993 Feld[x][y] = EL_AMOEBA_DROP;
12996 random = random * 129 + 1;
13002 if (game.explosions_delayed)
13005 game.explosions_delayed = FALSE;
13007 SCAN_PLAYFIELD(x, y)
13009 element = Feld[x][y];
13011 if (ExplodeField[x][y])
13012 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13013 else if (element == EL_EXPLOSION)
13014 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13016 ExplodeField[x][y] = EX_TYPE_NONE;
13019 game.explosions_delayed = TRUE;
13022 if (game.magic_wall_active)
13024 if (!(game.magic_wall_time_left % 4))
13026 int element = Feld[magic_wall_x][magic_wall_y];
13028 if (element == EL_BD_MAGIC_WALL_FULL ||
13029 element == EL_BD_MAGIC_WALL_ACTIVE ||
13030 element == EL_BD_MAGIC_WALL_EMPTYING)
13031 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13032 else if (element == EL_DC_MAGIC_WALL_FULL ||
13033 element == EL_DC_MAGIC_WALL_ACTIVE ||
13034 element == EL_DC_MAGIC_WALL_EMPTYING)
13035 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13037 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13040 if (game.magic_wall_time_left > 0)
13042 game.magic_wall_time_left--;
13044 if (!game.magic_wall_time_left)
13046 SCAN_PLAYFIELD(x, y)
13048 element = Feld[x][y];
13050 if (element == EL_MAGIC_WALL_ACTIVE ||
13051 element == EL_MAGIC_WALL_FULL)
13053 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13054 TEST_DrawLevelField(x, y);
13056 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13057 element == EL_BD_MAGIC_WALL_FULL)
13059 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13060 TEST_DrawLevelField(x, y);
13062 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13063 element == EL_DC_MAGIC_WALL_FULL)
13065 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13066 TEST_DrawLevelField(x, y);
13070 game.magic_wall_active = FALSE;
13075 if (game.light_time_left > 0)
13077 game.light_time_left--;
13079 if (game.light_time_left == 0)
13080 RedrawAllLightSwitchesAndInvisibleElements();
13083 if (game.timegate_time_left > 0)
13085 game.timegate_time_left--;
13087 if (game.timegate_time_left == 0)
13088 CloseAllOpenTimegates();
13091 if (game.lenses_time_left > 0)
13093 game.lenses_time_left--;
13095 if (game.lenses_time_left == 0)
13096 RedrawAllInvisibleElementsForLenses();
13099 if (game.magnify_time_left > 0)
13101 game.magnify_time_left--;
13103 if (game.magnify_time_left == 0)
13104 RedrawAllInvisibleElementsForMagnifier();
13107 for (i = 0; i < MAX_PLAYERS; i++)
13109 struct PlayerInfo *player = &stored_player[i];
13111 if (SHIELD_ON(player))
13113 if (player->shield_deadly_time_left)
13114 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13115 else if (player->shield_normal_time_left)
13116 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13120 #if USE_DELAYED_GFX_REDRAW
13121 SCAN_PLAYFIELD(x, y)
13124 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13126 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13127 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13130 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13131 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13133 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13134 DrawLevelField(x, y);
13136 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13137 DrawLevelFieldCrumbledSand(x, y);
13139 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13140 DrawLevelFieldCrumbledSandNeighbours(x, y);
13142 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13143 DrawTwinkleOnField(x, y);
13146 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13153 PlayAllPlayersSound();
13155 if (options.debug) /* calculate frames per second */
13157 static unsigned long fps_counter = 0;
13158 static int fps_frames = 0;
13159 unsigned long fps_delay_ms = Counter() - fps_counter;
13163 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13165 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13168 fps_counter = Counter();
13171 redraw_mask |= REDRAW_FPS;
13174 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13176 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13178 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13180 local_player->show_envelope = 0;
13184 debug_print_timestamp(0, "stop main loop profiling ");
13185 printf("----------------------------------------------------------\n");
13188 /* use random number generator in every frame to make it less predictable */
13189 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13193 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13195 int min_x = x, min_y = y, max_x = x, max_y = y;
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 || &stored_player[i] == player)
13205 min_x = MIN(min_x, jx);
13206 min_y = MIN(min_y, jy);
13207 max_x = MAX(max_x, jx);
13208 max_y = MAX(max_y, jy);
13211 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13214 static boolean AllPlayersInVisibleScreen()
13218 for (i = 0; i < MAX_PLAYERS; i++)
13220 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13222 if (!stored_player[i].active)
13225 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13232 void ScrollLevel(int dx, int dy)
13235 /* (directly solved in BlitBitmap() now) */
13236 static Bitmap *bitmap_db_field2 = NULL;
13237 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13244 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13245 /* only horizontal XOR vertical scroll direction allowed */
13246 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13251 /* (directly solved in BlitBitmap() now) */
13252 if (bitmap_db_field2 == NULL)
13253 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13255 /* needed when blitting directly to same bitmap -- should not be needed with
13256 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13257 BlitBitmap(drawto_field, bitmap_db_field2,
13258 FX + TILEX * (dx == -1) - softscroll_offset,
13259 FY + TILEY * (dy == -1) - softscroll_offset,
13260 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13261 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13262 FX + TILEX * (dx == 1) - softscroll_offset,
13263 FY + TILEY * (dy == 1) - softscroll_offset);
13264 BlitBitmap(bitmap_db_field2, drawto_field,
13265 FX + TILEX * (dx == 1) - softscroll_offset,
13266 FY + TILEY * (dy == 1) - softscroll_offset,
13267 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13268 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13269 FX + TILEX * (dx == 1) - softscroll_offset,
13270 FY + TILEY * (dy == 1) - softscroll_offset);
13275 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13276 int xsize = (BX2 - BX1 + 1);
13277 int ysize = (BY2 - BY1 + 1);
13278 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13279 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13280 int step = (start < end ? +1 : -1);
13282 for (i = start; i != end; i += step)
13284 BlitBitmap(drawto_field, drawto_field,
13285 FX + TILEX * (dx != 0 ? i + step : 0),
13286 FY + TILEY * (dy != 0 ? i + step : 0),
13287 TILEX * (dx != 0 ? 1 : xsize),
13288 TILEY * (dy != 0 ? 1 : ysize),
13289 FX + TILEX * (dx != 0 ? i : 0),
13290 FY + TILEY * (dy != 0 ? i : 0));
13295 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13297 BlitBitmap(drawto_field, drawto_field,
13298 FX + TILEX * (dx == -1) - softscroll_offset,
13299 FY + TILEY * (dy == -1) - softscroll_offset,
13300 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13301 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13302 FX + TILEX * (dx == 1) - softscroll_offset,
13303 FY + TILEY * (dy == 1) - softscroll_offset);
13309 x = (dx == 1 ? BX1 : BX2);
13310 for (y = BY1; y <= BY2; y++)
13311 DrawScreenField(x, y);
13316 y = (dy == 1 ? BY1 : BY2);
13317 for (x = BX1; x <= BX2; x++)
13318 DrawScreenField(x, y);
13321 redraw_mask |= REDRAW_FIELD;
13324 static boolean canFallDown(struct PlayerInfo *player)
13326 int jx = player->jx, jy = player->jy;
13328 return (IN_LEV_FIELD(jx, jy + 1) &&
13329 (IS_FREE(jx, jy + 1) ||
13330 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13331 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13332 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13335 static boolean canPassField(int x, int y, int move_dir)
13337 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13338 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13339 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13340 int nextx = x + dx;
13341 int nexty = y + dy;
13342 int element = Feld[x][y];
13344 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13345 !CAN_MOVE(element) &&
13346 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13347 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13348 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13351 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13353 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13354 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13355 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13359 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13360 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13361 (IS_DIGGABLE(Feld[newx][newy]) ||
13362 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13363 canPassField(newx, newy, move_dir)));
13366 static void CheckGravityMovement(struct PlayerInfo *player)
13368 #if USE_PLAYER_GRAVITY
13369 if (player->gravity && !player->programmed_action)
13371 if (game.gravity && !player->programmed_action)
13374 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13375 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13376 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13377 int jx = player->jx, jy = player->jy;
13378 boolean player_is_moving_to_valid_field =
13379 (!player_is_snapping &&
13380 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13381 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13382 boolean player_can_fall_down = canFallDown(player);
13384 if (player_can_fall_down &&
13385 !player_is_moving_to_valid_field)
13386 player->programmed_action = MV_DOWN;
13390 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13392 return CheckGravityMovement(player);
13394 #if USE_PLAYER_GRAVITY
13395 if (player->gravity && !player->programmed_action)
13397 if (game.gravity && !player->programmed_action)
13400 int jx = player->jx, jy = player->jy;
13401 boolean field_under_player_is_free =
13402 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13403 boolean player_is_standing_on_valid_field =
13404 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13405 (IS_WALKABLE(Feld[jx][jy]) &&
13406 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13408 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13409 player->programmed_action = MV_DOWN;
13414 MovePlayerOneStep()
13415 -----------------------------------------------------------------------------
13416 dx, dy: direction (non-diagonal) to try to move the player to
13417 real_dx, real_dy: direction as read from input device (can be diagonal)
13420 boolean MovePlayerOneStep(struct PlayerInfo *player,
13421 int dx, int dy, int real_dx, int real_dy)
13423 int jx = player->jx, jy = player->jy;
13424 int new_jx = jx + dx, new_jy = jy + dy;
13425 #if !USE_FIXED_DONT_RUN_INTO
13429 boolean player_can_move = !player->cannot_move;
13431 if (!player->active || (!dx && !dy))
13432 return MP_NO_ACTION;
13434 player->MovDir = (dx < 0 ? MV_LEFT :
13435 dx > 0 ? MV_RIGHT :
13437 dy > 0 ? MV_DOWN : MV_NONE);
13439 if (!IN_LEV_FIELD(new_jx, new_jy))
13440 return MP_NO_ACTION;
13442 if (!player_can_move)
13444 if (player->MovPos == 0)
13446 player->is_moving = FALSE;
13447 player->is_digging = FALSE;
13448 player->is_collecting = FALSE;
13449 player->is_snapping = FALSE;
13450 player->is_pushing = FALSE;
13455 if (!options.network && game.centered_player_nr == -1 &&
13456 !AllPlayersInSight(player, new_jx, new_jy))
13457 return MP_NO_ACTION;
13459 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13460 return MP_NO_ACTION;
13463 #if !USE_FIXED_DONT_RUN_INTO
13464 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13466 /* (moved to DigField()) */
13467 if (player_can_move && DONT_RUN_INTO(element))
13469 if (element == EL_ACID && dx == 0 && dy == 1)
13471 SplashAcid(new_jx, new_jy);
13472 Feld[jx][jy] = EL_PLAYER_1;
13473 InitMovingField(jx, jy, MV_DOWN);
13474 Store[jx][jy] = EL_ACID;
13475 ContinueMoving(jx, jy);
13476 BuryPlayer(player);
13479 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13485 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13486 if (can_move != MP_MOVING)
13489 /* check if DigField() has caused relocation of the player */
13490 if (player->jx != jx || player->jy != jy)
13491 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13493 StorePlayer[jx][jy] = 0;
13494 player->last_jx = jx;
13495 player->last_jy = jy;
13496 player->jx = new_jx;
13497 player->jy = new_jy;
13498 StorePlayer[new_jx][new_jy] = player->element_nr;
13500 if (player->move_delay_value_next != -1)
13502 player->move_delay_value = player->move_delay_value_next;
13503 player->move_delay_value_next = -1;
13507 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13509 player->step_counter++;
13511 PlayerVisit[jx][jy] = FrameCounter;
13513 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13514 player->is_moving = TRUE;
13518 /* should better be called in MovePlayer(), but this breaks some tapes */
13519 ScrollPlayer(player, SCROLL_INIT);
13525 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13527 int jx = player->jx, jy = player->jy;
13528 int old_jx = jx, old_jy = jy;
13529 int moved = MP_NO_ACTION;
13531 if (!player->active)
13536 if (player->MovPos == 0)
13538 player->is_moving = FALSE;
13539 player->is_digging = FALSE;
13540 player->is_collecting = FALSE;
13541 player->is_snapping = FALSE;
13542 player->is_pushing = FALSE;
13548 if (player->move_delay > 0)
13551 player->move_delay = -1; /* set to "uninitialized" value */
13553 /* store if player is automatically moved to next field */
13554 player->is_auto_moving = (player->programmed_action != MV_NONE);
13556 /* remove the last programmed player action */
13557 player->programmed_action = 0;
13559 if (player->MovPos)
13561 /* should only happen if pre-1.2 tape recordings are played */
13562 /* this is only for backward compatibility */
13564 int original_move_delay_value = player->move_delay_value;
13567 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13571 /* scroll remaining steps with finest movement resolution */
13572 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13574 while (player->MovPos)
13576 ScrollPlayer(player, SCROLL_GO_ON);
13577 ScrollScreen(NULL, SCROLL_GO_ON);
13579 AdvanceFrameAndPlayerCounters(player->index_nr);
13585 player->move_delay_value = original_move_delay_value;
13588 player->is_active = FALSE;
13590 if (player->last_move_dir & MV_HORIZONTAL)
13592 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13593 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13597 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13598 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13601 #if USE_FIXED_BORDER_RUNNING_GFX
13602 if (!moved && !player->is_active)
13604 player->is_moving = FALSE;
13605 player->is_digging = FALSE;
13606 player->is_collecting = FALSE;
13607 player->is_snapping = FALSE;
13608 player->is_pushing = FALSE;
13616 if (moved & MP_MOVING && !ScreenMovPos &&
13617 (player->index_nr == game.centered_player_nr ||
13618 game.centered_player_nr == -1))
13620 if (moved & MP_MOVING && !ScreenMovPos &&
13621 (player == local_player || !options.network))
13624 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13625 int offset = game.scroll_delay_value;
13627 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13629 /* actual player has left the screen -- scroll in that direction */
13630 if (jx != old_jx) /* player has moved horizontally */
13631 scroll_x += (jx - old_jx);
13632 else /* player has moved vertically */
13633 scroll_y += (jy - old_jy);
13637 if (jx != old_jx) /* player has moved horizontally */
13639 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13640 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13641 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13643 /* don't scroll over playfield boundaries */
13644 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13645 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13647 /* don't scroll more than one field at a time */
13648 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13650 /* don't scroll against the player's moving direction */
13651 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13652 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13653 scroll_x = old_scroll_x;
13655 else /* player has moved vertically */
13657 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13658 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13659 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13661 /* don't scroll over playfield boundaries */
13662 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13663 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13665 /* don't scroll more than one field at a time */
13666 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13668 /* don't scroll against the player's moving direction */
13669 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13670 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13671 scroll_y = old_scroll_y;
13675 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13678 if (!options.network && game.centered_player_nr == -1 &&
13679 !AllPlayersInVisibleScreen())
13681 scroll_x = old_scroll_x;
13682 scroll_y = old_scroll_y;
13686 if (!options.network && !AllPlayersInVisibleScreen())
13688 scroll_x = old_scroll_x;
13689 scroll_y = old_scroll_y;
13694 ScrollScreen(player, SCROLL_INIT);
13695 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13700 player->StepFrame = 0;
13702 if (moved & MP_MOVING)
13704 if (old_jx != jx && old_jy == jy)
13705 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13706 else if (old_jx == jx && old_jy != jy)
13707 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13709 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13711 player->last_move_dir = player->MovDir;
13712 player->is_moving = TRUE;
13713 player->is_snapping = FALSE;
13714 player->is_switching = FALSE;
13715 player->is_dropping = FALSE;
13716 player->is_dropping_pressed = FALSE;
13717 player->drop_pressed_delay = 0;
13720 /* should better be called here than above, but this breaks some tapes */
13721 ScrollPlayer(player, SCROLL_INIT);
13726 CheckGravityMovementWhenNotMoving(player);
13728 player->is_moving = FALSE;
13730 /* at this point, the player is allowed to move, but cannot move right now
13731 (e.g. because of something blocking the way) -- ensure that the player
13732 is also allowed to move in the next frame (in old versions before 3.1.1,
13733 the player was forced to wait again for eight frames before next try) */
13735 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13736 player->move_delay = 0; /* allow direct movement in the next frame */
13739 if (player->move_delay == -1) /* not yet initialized by DigField() */
13740 player->move_delay = player->move_delay_value;
13742 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13744 TestIfPlayerTouchesBadThing(jx, jy);
13745 TestIfPlayerTouchesCustomElement(jx, jy);
13748 if (!player->active)
13749 RemovePlayer(player);
13754 void ScrollPlayer(struct PlayerInfo *player, int mode)
13756 int jx = player->jx, jy = player->jy;
13757 int last_jx = player->last_jx, last_jy = player->last_jy;
13758 int move_stepsize = TILEX / player->move_delay_value;
13760 #if USE_NEW_PLAYER_SPEED
13761 if (!player->active)
13764 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13767 if (!player->active || player->MovPos == 0)
13771 if (mode == SCROLL_INIT)
13773 player->actual_frame_counter = FrameCounter;
13774 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13776 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13777 Feld[last_jx][last_jy] == EL_EMPTY)
13779 int last_field_block_delay = 0; /* start with no blocking at all */
13780 int block_delay_adjustment = player->block_delay_adjustment;
13782 /* if player blocks last field, add delay for exactly one move */
13783 if (player->block_last_field)
13785 last_field_block_delay += player->move_delay_value;
13787 /* when blocking enabled, prevent moving up despite gravity */
13788 #if USE_PLAYER_GRAVITY
13789 if (player->gravity && player->MovDir == MV_UP)
13790 block_delay_adjustment = -1;
13792 if (game.gravity && player->MovDir == MV_UP)
13793 block_delay_adjustment = -1;
13797 /* add block delay adjustment (also possible when not blocking) */
13798 last_field_block_delay += block_delay_adjustment;
13800 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13801 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13804 #if USE_NEW_PLAYER_SPEED
13805 if (player->MovPos != 0) /* player has not yet reached destination */
13811 else if (!FrameReached(&player->actual_frame_counter, 1))
13814 #if USE_NEW_PLAYER_SPEED
13815 if (player->MovPos != 0)
13817 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13818 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13820 /* before DrawPlayer() to draw correct player graphic for this case */
13821 if (player->MovPos == 0)
13822 CheckGravityMovement(player);
13825 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13826 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13828 /* before DrawPlayer() to draw correct player graphic for this case */
13829 if (player->MovPos == 0)
13830 CheckGravityMovement(player);
13833 if (player->MovPos == 0) /* player reached destination field */
13835 if (player->move_delay_reset_counter > 0)
13837 player->move_delay_reset_counter--;
13839 if (player->move_delay_reset_counter == 0)
13841 /* continue with normal speed after quickly moving through gate */
13842 HALVE_PLAYER_SPEED(player);
13844 /* be able to make the next move without delay */
13845 player->move_delay = 0;
13849 player->last_jx = jx;
13850 player->last_jy = jy;
13852 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13853 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13855 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13857 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13858 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13860 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13862 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13863 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13865 DrawPlayer(player); /* needed here only to cleanup last field */
13866 RemovePlayer(player);
13868 if (local_player->friends_still_needed == 0 ||
13869 IS_SP_ELEMENT(Feld[jx][jy]))
13870 PlayerWins(player);
13873 /* this breaks one level: "machine", level 000 */
13875 int move_direction = player->MovDir;
13876 int enter_side = MV_DIR_OPPOSITE(move_direction);
13877 int leave_side = move_direction;
13878 int old_jx = last_jx;
13879 int old_jy = last_jy;
13880 int old_element = Feld[old_jx][old_jy];
13881 int new_element = Feld[jx][jy];
13883 if (IS_CUSTOM_ELEMENT(old_element))
13884 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13886 player->index_bit, leave_side);
13888 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13889 CE_PLAYER_LEAVES_X,
13890 player->index_bit, leave_side);
13892 if (IS_CUSTOM_ELEMENT(new_element))
13893 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13894 player->index_bit, enter_side);
13896 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13897 CE_PLAYER_ENTERS_X,
13898 player->index_bit, enter_side);
13900 #if USE_FIX_CE_ACTION_WITH_PLAYER
13901 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13902 CE_MOVE_OF_X, move_direction);
13904 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13905 CE_MOVE_OF_X, move_direction);
13909 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13911 TestIfPlayerTouchesBadThing(jx, jy);
13912 TestIfPlayerTouchesCustomElement(jx, jy);
13914 /* needed because pushed element has not yet reached its destination,
13915 so it would trigger a change event at its previous field location */
13916 if (!player->is_pushing)
13917 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13919 if (!player->active)
13920 RemovePlayer(player);
13923 if (!local_player->LevelSolved && level.use_step_counter)
13933 if (TimeLeft <= 10 && setup.time_limit)
13934 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13937 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13939 DisplayGameControlValues();
13941 DrawGameValue_Time(TimeLeft);
13944 if (!TimeLeft && setup.time_limit)
13945 for (i = 0; i < MAX_PLAYERS; i++)
13946 KillPlayer(&stored_player[i]);
13949 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13951 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13953 DisplayGameControlValues();
13956 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13957 DrawGameValue_Time(TimePlayed);
13961 if (tape.single_step && tape.recording && !tape.pausing &&
13962 !player->programmed_action)
13963 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13967 void ScrollScreen(struct PlayerInfo *player, int mode)
13969 static unsigned long screen_frame_counter = 0;
13971 if (mode == SCROLL_INIT)
13973 /* set scrolling step size according to actual player's moving speed */
13974 ScrollStepSize = TILEX / player->move_delay_value;
13976 screen_frame_counter = FrameCounter;
13977 ScreenMovDir = player->MovDir;
13978 ScreenMovPos = player->MovPos;
13979 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13982 else if (!FrameReached(&screen_frame_counter, 1))
13987 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13988 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13989 redraw_mask |= REDRAW_FIELD;
13992 ScreenMovDir = MV_NONE;
13995 void TestIfPlayerTouchesCustomElement(int x, int y)
13997 static int xy[4][2] =
14004 static int trigger_sides[4][2] =
14006 /* center side border side */
14007 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14008 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14009 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14010 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14012 static int touch_dir[4] =
14014 MV_LEFT | MV_RIGHT,
14019 int center_element = Feld[x][y]; /* should always be non-moving! */
14022 for (i = 0; i < NUM_DIRECTIONS; i++)
14024 int xx = x + xy[i][0];
14025 int yy = y + xy[i][1];
14026 int center_side = trigger_sides[i][0];
14027 int border_side = trigger_sides[i][1];
14028 int border_element;
14030 if (!IN_LEV_FIELD(xx, yy))
14033 if (IS_PLAYER(x, y)) /* player found at center element */
14035 struct PlayerInfo *player = PLAYERINFO(x, y);
14037 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14038 border_element = Feld[xx][yy]; /* may be moving! */
14039 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14040 border_element = Feld[xx][yy];
14041 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14042 border_element = MovingOrBlocked2Element(xx, yy);
14044 continue; /* center and border element do not touch */
14046 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14047 player->index_bit, border_side);
14048 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14049 CE_PLAYER_TOUCHES_X,
14050 player->index_bit, border_side);
14052 #if USE_FIX_CE_ACTION_WITH_PLAYER
14054 /* use player element that is initially defined in the level playfield,
14055 not the player element that corresponds to the runtime player number
14056 (example: a level that contains EL_PLAYER_3 as the only player would
14057 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14058 int player_element = PLAYERINFO(x, y)->initial_element;
14060 CheckElementChangeBySide(xx, yy, border_element, player_element,
14061 CE_TOUCHING_X, border_side);
14065 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14067 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14069 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14071 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14072 continue; /* center and border element do not touch */
14075 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14076 player->index_bit, center_side);
14077 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14078 CE_PLAYER_TOUCHES_X,
14079 player->index_bit, center_side);
14081 #if USE_FIX_CE_ACTION_WITH_PLAYER
14083 /* use player element that is initially defined in the level playfield,
14084 not the player element that corresponds to the runtime player number
14085 (example: a level that contains EL_PLAYER_3 as the only player would
14086 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14087 int player_element = PLAYERINFO(xx, yy)->initial_element;
14089 CheckElementChangeBySide(x, y, center_element, player_element,
14090 CE_TOUCHING_X, center_side);
14099 #if USE_ELEMENT_TOUCHING_BUGFIX
14101 void TestIfElementTouchesCustomElement(int x, int y)
14103 static int xy[4][2] =
14110 static int trigger_sides[4][2] =
14112 /* center side border side */
14113 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14114 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14115 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14116 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14118 static int touch_dir[4] =
14120 MV_LEFT | MV_RIGHT,
14125 boolean change_center_element = FALSE;
14126 int center_element = Feld[x][y]; /* should always be non-moving! */
14127 int border_element_old[NUM_DIRECTIONS];
14130 for (i = 0; i < NUM_DIRECTIONS; i++)
14132 int xx = x + xy[i][0];
14133 int yy = y + xy[i][1];
14134 int border_element;
14136 border_element_old[i] = -1;
14138 if (!IN_LEV_FIELD(xx, yy))
14141 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14142 border_element = Feld[xx][yy]; /* may be moving! */
14143 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14144 border_element = Feld[xx][yy];
14145 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14146 border_element = MovingOrBlocked2Element(xx, yy);
14148 continue; /* center and border element do not touch */
14150 border_element_old[i] = border_element;
14153 for (i = 0; i < NUM_DIRECTIONS; i++)
14155 int xx = x + xy[i][0];
14156 int yy = y + xy[i][1];
14157 int center_side = trigger_sides[i][0];
14158 int border_element = border_element_old[i];
14160 if (border_element == -1)
14163 /* check for change of border element */
14164 CheckElementChangeBySide(xx, yy, border_element, center_element,
14165 CE_TOUCHING_X, center_side);
14167 /* (center element cannot be player, so we dont have to check this here) */
14170 for (i = 0; i < NUM_DIRECTIONS; i++)
14172 int xx = x + xy[i][0];
14173 int yy = y + xy[i][1];
14174 int border_side = trigger_sides[i][1];
14175 int border_element = border_element_old[i];
14177 if (border_element == -1)
14180 /* check for change of center element (but change it only once) */
14181 if (!change_center_element)
14182 change_center_element =
14183 CheckElementChangeBySide(x, y, center_element, border_element,
14184 CE_TOUCHING_X, border_side);
14186 #if USE_FIX_CE_ACTION_WITH_PLAYER
14187 if (IS_PLAYER(xx, yy))
14189 /* use player element that is initially defined in the level playfield,
14190 not the player element that corresponds to the runtime player number
14191 (example: a level that contains EL_PLAYER_3 as the only player would
14192 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14193 int player_element = PLAYERINFO(xx, yy)->initial_element;
14195 CheckElementChangeBySide(x, y, center_element, player_element,
14196 CE_TOUCHING_X, border_side);
14204 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14206 static int xy[4][2] =
14213 static int trigger_sides[4][2] =
14215 /* center side border side */
14216 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14217 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14218 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14219 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14221 static int touch_dir[4] =
14223 MV_LEFT | MV_RIGHT,
14228 boolean change_center_element = FALSE;
14229 int center_element = Feld[x][y]; /* should always be non-moving! */
14232 for (i = 0; i < NUM_DIRECTIONS; i++)
14234 int xx = x + xy[i][0];
14235 int yy = y + xy[i][1];
14236 int center_side = trigger_sides[i][0];
14237 int border_side = trigger_sides[i][1];
14238 int border_element;
14240 if (!IN_LEV_FIELD(xx, yy))
14243 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14244 border_element = Feld[xx][yy]; /* may be moving! */
14245 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14246 border_element = Feld[xx][yy];
14247 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14248 border_element = MovingOrBlocked2Element(xx, yy);
14250 continue; /* center and border element do not touch */
14252 /* check for change of center element (but change it only once) */
14253 if (!change_center_element)
14254 change_center_element =
14255 CheckElementChangeBySide(x, y, center_element, border_element,
14256 CE_TOUCHING_X, border_side);
14258 /* check for change of border element */
14259 CheckElementChangeBySide(xx, yy, border_element, center_element,
14260 CE_TOUCHING_X, center_side);
14266 void TestIfElementHitsCustomElement(int x, int y, int direction)
14268 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14269 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14270 int hitx = x + dx, hity = y + dy;
14271 int hitting_element = Feld[x][y];
14272 int touched_element;
14274 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14277 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14278 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14280 if (IN_LEV_FIELD(hitx, hity))
14282 int opposite_direction = MV_DIR_OPPOSITE(direction);
14283 int hitting_side = direction;
14284 int touched_side = opposite_direction;
14285 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14286 MovDir[hitx][hity] != direction ||
14287 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14293 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14294 CE_HITTING_X, touched_side);
14296 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14297 CE_HIT_BY_X, hitting_side);
14299 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14300 CE_HIT_BY_SOMETHING, opposite_direction);
14302 #if USE_FIX_CE_ACTION_WITH_PLAYER
14303 if (IS_PLAYER(hitx, hity))
14305 /* use player element that is initially defined in the level playfield,
14306 not the player element that corresponds to the runtime player number
14307 (example: a level that contains EL_PLAYER_3 as the only player would
14308 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14309 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14311 CheckElementChangeBySide(x, y, hitting_element, player_element,
14312 CE_HITTING_X, touched_side);
14318 /* "hitting something" is also true when hitting the playfield border */
14319 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14320 CE_HITTING_SOMETHING, direction);
14324 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14326 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14327 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14328 int hitx = x + dx, hity = y + dy;
14329 int hitting_element = Feld[x][y];
14330 int touched_element;
14332 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14333 !IS_FREE(hitx, hity) &&
14334 (!IS_MOVING(hitx, hity) ||
14335 MovDir[hitx][hity] != direction ||
14336 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14339 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14343 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14347 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14348 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14350 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14351 EP_CAN_SMASH_EVERYTHING, direction);
14353 if (IN_LEV_FIELD(hitx, hity))
14355 int opposite_direction = MV_DIR_OPPOSITE(direction);
14356 int hitting_side = direction;
14357 int touched_side = opposite_direction;
14359 int touched_element = MovingOrBlocked2Element(hitx, hity);
14362 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14363 MovDir[hitx][hity] != direction ||
14364 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14373 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14374 CE_SMASHED_BY_SOMETHING, opposite_direction);
14376 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14377 CE_OTHER_IS_SMASHING, touched_side);
14379 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14380 CE_OTHER_GETS_SMASHED, hitting_side);
14386 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14388 int i, kill_x = -1, kill_y = -1;
14390 int bad_element = -1;
14391 static int test_xy[4][2] =
14398 static int test_dir[4] =
14406 for (i = 0; i < NUM_DIRECTIONS; i++)
14408 int test_x, test_y, test_move_dir, test_element;
14410 test_x = good_x + test_xy[i][0];
14411 test_y = good_y + test_xy[i][1];
14413 if (!IN_LEV_FIELD(test_x, test_y))
14417 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14419 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14421 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14422 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14424 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14425 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14429 bad_element = test_element;
14435 if (kill_x != -1 || kill_y != -1)
14437 if (IS_PLAYER(good_x, good_y))
14439 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14441 if (player->shield_deadly_time_left > 0 &&
14442 !IS_INDESTRUCTIBLE(bad_element))
14443 Bang(kill_x, kill_y);
14444 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14445 KillPlayer(player);
14448 Bang(good_x, good_y);
14452 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14454 int i, kill_x = -1, kill_y = -1;
14455 int bad_element = Feld[bad_x][bad_y];
14456 static int test_xy[4][2] =
14463 static int touch_dir[4] =
14465 MV_LEFT | MV_RIGHT,
14470 static int test_dir[4] =
14478 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14481 for (i = 0; i < NUM_DIRECTIONS; i++)
14483 int test_x, test_y, test_move_dir, test_element;
14485 test_x = bad_x + test_xy[i][0];
14486 test_y = bad_y + test_xy[i][1];
14488 if (!IN_LEV_FIELD(test_x, test_y))
14492 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14494 test_element = Feld[test_x][test_y];
14496 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14497 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14499 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14500 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14502 /* good thing is player or penguin that does not move away */
14503 if (IS_PLAYER(test_x, test_y))
14505 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14507 if (bad_element == EL_ROBOT && player->is_moving)
14508 continue; /* robot does not kill player if he is moving */
14510 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14512 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14513 continue; /* center and border element do not touch */
14521 else if (test_element == EL_PENGUIN)
14531 if (kill_x != -1 || kill_y != -1)
14533 if (IS_PLAYER(kill_x, kill_y))
14535 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14537 if (player->shield_deadly_time_left > 0 &&
14538 !IS_INDESTRUCTIBLE(bad_element))
14539 Bang(bad_x, bad_y);
14540 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14541 KillPlayer(player);
14544 Bang(kill_x, kill_y);
14548 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14550 int bad_element = Feld[bad_x][bad_y];
14551 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14552 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14553 int test_x = bad_x + dx, test_y = bad_y + dy;
14554 int test_move_dir, test_element;
14555 int kill_x = -1, kill_y = -1;
14557 if (!IN_LEV_FIELD(test_x, test_y))
14561 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14563 test_element = Feld[test_x][test_y];
14565 if (test_move_dir != bad_move_dir)
14567 /* good thing can be player or penguin that does not move away */
14568 if (IS_PLAYER(test_x, test_y))
14570 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14572 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14573 player as being hit when he is moving towards the bad thing, because
14574 the "get hit by" condition would be lost after the player stops) */
14575 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14576 return; /* player moves away from bad thing */
14581 else if (test_element == EL_PENGUIN)
14588 if (kill_x != -1 || kill_y != -1)
14590 if (IS_PLAYER(kill_x, kill_y))
14592 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14594 if (player->shield_deadly_time_left > 0 &&
14595 !IS_INDESTRUCTIBLE(bad_element))
14596 Bang(bad_x, bad_y);
14597 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14598 KillPlayer(player);
14601 Bang(kill_x, kill_y);
14605 void TestIfPlayerTouchesBadThing(int x, int y)
14607 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14610 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14612 TestIfGoodThingHitsBadThing(x, y, move_dir);
14615 void TestIfBadThingTouchesPlayer(int x, int y)
14617 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14620 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14622 TestIfBadThingHitsGoodThing(x, y, move_dir);
14625 void TestIfFriendTouchesBadThing(int x, int y)
14627 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14630 void TestIfBadThingTouchesFriend(int x, int y)
14632 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14635 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14637 int i, kill_x = bad_x, kill_y = bad_y;
14638 static int xy[4][2] =
14646 for (i = 0; i < NUM_DIRECTIONS; i++)
14650 x = bad_x + xy[i][0];
14651 y = bad_y + xy[i][1];
14652 if (!IN_LEV_FIELD(x, y))
14655 element = Feld[x][y];
14656 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14657 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14665 if (kill_x != bad_x || kill_y != bad_y)
14666 Bang(bad_x, bad_y);
14669 void KillPlayer(struct PlayerInfo *player)
14671 int jx = player->jx, jy = player->jy;
14673 if (!player->active)
14677 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14678 player->killed, player->active, player->reanimated);
14681 /* the following code was introduced to prevent an infinite loop when calling
14683 -> CheckTriggeredElementChangeExt()
14684 -> ExecuteCustomElementAction()
14686 -> (infinitely repeating the above sequence of function calls)
14687 which occurs when killing the player while having a CE with the setting
14688 "kill player X when explosion of <player X>"; the solution using a new
14689 field "player->killed" was chosen for backwards compatibility, although
14690 clever use of the fields "player->active" etc. would probably also work */
14692 if (player->killed)
14696 player->killed = TRUE;
14698 /* remove accessible field at the player's position */
14699 Feld[jx][jy] = EL_EMPTY;
14701 /* deactivate shield (else Bang()/Explode() would not work right) */
14702 player->shield_normal_time_left = 0;
14703 player->shield_deadly_time_left = 0;
14706 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14707 player->killed, player->active, player->reanimated);
14713 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14714 player->killed, player->active, player->reanimated);
14717 #if USE_PLAYER_REANIMATION
14719 if (player->reanimated) /* killed player may have been reanimated */
14720 player->killed = player->reanimated = FALSE;
14722 BuryPlayer(player);
14724 if (player->killed) /* player may have been reanimated */
14725 BuryPlayer(player);
14728 BuryPlayer(player);
14732 static void KillPlayerUnlessEnemyProtected(int x, int y)
14734 if (!PLAYER_ENEMY_PROTECTED(x, y))
14735 KillPlayer(PLAYERINFO(x, y));
14738 static void KillPlayerUnlessExplosionProtected(int x, int y)
14740 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14741 KillPlayer(PLAYERINFO(x, y));
14744 void BuryPlayer(struct PlayerInfo *player)
14746 int jx = player->jx, jy = player->jy;
14748 if (!player->active)
14751 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14752 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14754 player->GameOver = TRUE;
14755 RemovePlayer(player);
14758 void RemovePlayer(struct PlayerInfo *player)
14760 int jx = player->jx, jy = player->jy;
14761 int i, found = FALSE;
14763 player->present = FALSE;
14764 player->active = FALSE;
14766 if (!ExplodeField[jx][jy])
14767 StorePlayer[jx][jy] = 0;
14769 if (player->is_moving)
14770 TEST_DrawLevelField(player->last_jx, player->last_jy);
14772 for (i = 0; i < MAX_PLAYERS; i++)
14773 if (stored_player[i].active)
14777 AllPlayersGone = TRUE;
14783 #if USE_NEW_SNAP_DELAY
14784 static void setFieldForSnapping(int x, int y, int element, int direction)
14786 struct ElementInfo *ei = &element_info[element];
14787 int direction_bit = MV_DIR_TO_BIT(direction);
14788 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14789 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14790 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14792 Feld[x][y] = EL_ELEMENT_SNAPPING;
14793 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14795 ResetGfxAnimation(x, y);
14797 GfxElement[x][y] = element;
14798 GfxAction[x][y] = action;
14799 GfxDir[x][y] = direction;
14800 GfxFrame[x][y] = -1;
14805 =============================================================================
14806 checkDiagonalPushing()
14807 -----------------------------------------------------------------------------
14808 check if diagonal input device direction results in pushing of object
14809 (by checking if the alternative direction is walkable, diggable, ...)
14810 =============================================================================
14813 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14814 int x, int y, int real_dx, int real_dy)
14816 int jx, jy, dx, dy, xx, yy;
14818 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14821 /* diagonal direction: check alternative direction */
14826 xx = jx + (dx == 0 ? real_dx : 0);
14827 yy = jy + (dy == 0 ? real_dy : 0);
14829 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14833 =============================================================================
14835 -----------------------------------------------------------------------------
14836 x, y: field next to player (non-diagonal) to try to dig to
14837 real_dx, real_dy: direction as read from input device (can be diagonal)
14838 =============================================================================
14841 static int DigField(struct PlayerInfo *player,
14842 int oldx, int oldy, int x, int y,
14843 int real_dx, int real_dy, int mode)
14845 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14846 boolean player_was_pushing = player->is_pushing;
14847 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14848 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14849 int jx = oldx, jy = oldy;
14850 int dx = x - jx, dy = y - jy;
14851 int nextx = x + dx, nexty = y + dy;
14852 int move_direction = (dx == -1 ? MV_LEFT :
14853 dx == +1 ? MV_RIGHT :
14855 dy == +1 ? MV_DOWN : MV_NONE);
14856 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14857 int dig_side = MV_DIR_OPPOSITE(move_direction);
14858 int old_element = Feld[jx][jy];
14859 #if USE_FIXED_DONT_RUN_INTO
14860 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14866 if (is_player) /* function can also be called by EL_PENGUIN */
14868 if (player->MovPos == 0)
14870 player->is_digging = FALSE;
14871 player->is_collecting = FALSE;
14874 if (player->MovPos == 0) /* last pushing move finished */
14875 player->is_pushing = FALSE;
14877 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14879 player->is_switching = FALSE;
14880 player->push_delay = -1;
14882 return MP_NO_ACTION;
14886 #if !USE_FIXED_DONT_RUN_INTO
14887 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14888 return MP_NO_ACTION;
14891 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14892 old_element = Back[jx][jy];
14894 /* in case of element dropped at player position, check background */
14895 else if (Back[jx][jy] != EL_EMPTY &&
14896 game.engine_version >= VERSION_IDENT(2,2,0,0))
14897 old_element = Back[jx][jy];
14899 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14900 return MP_NO_ACTION; /* field has no opening in this direction */
14902 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14903 return MP_NO_ACTION; /* field has no opening in this direction */
14905 #if USE_FIXED_DONT_RUN_INTO
14906 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14910 Feld[jx][jy] = player->artwork_element;
14911 InitMovingField(jx, jy, MV_DOWN);
14912 Store[jx][jy] = EL_ACID;
14913 ContinueMoving(jx, jy);
14914 BuryPlayer(player);
14916 return MP_DONT_RUN_INTO;
14920 #if USE_FIXED_DONT_RUN_INTO
14921 if (player_can_move && DONT_RUN_INTO(element))
14923 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14925 return MP_DONT_RUN_INTO;
14929 #if USE_FIXED_DONT_RUN_INTO
14930 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14931 return MP_NO_ACTION;
14934 #if !USE_FIXED_DONT_RUN_INTO
14935 element = Feld[x][y];
14938 collect_count = element_info[element].collect_count_initial;
14940 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14941 return MP_NO_ACTION;
14943 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14944 player_can_move = player_can_move_or_snap;
14946 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14947 game.engine_version >= VERSION_IDENT(2,2,0,0))
14949 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14950 player->index_bit, dig_side);
14951 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14952 player->index_bit, dig_side);
14954 if (element == EL_DC_LANDMINE)
14957 if (Feld[x][y] != element) /* field changed by snapping */
14960 return MP_NO_ACTION;
14963 #if USE_PLAYER_GRAVITY
14964 if (player->gravity && is_player && !player->is_auto_moving &&
14965 canFallDown(player) && move_direction != MV_DOWN &&
14966 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14967 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14969 if (game.gravity && is_player && !player->is_auto_moving &&
14970 canFallDown(player) && move_direction != MV_DOWN &&
14971 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14972 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14975 if (player_can_move &&
14976 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14978 int sound_element = SND_ELEMENT(element);
14979 int sound_action = ACTION_WALKING;
14981 if (IS_RND_GATE(element))
14983 if (!player->key[RND_GATE_NR(element)])
14984 return MP_NO_ACTION;
14986 else if (IS_RND_GATE_GRAY(element))
14988 if (!player->key[RND_GATE_GRAY_NR(element)])
14989 return MP_NO_ACTION;
14991 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14993 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14994 return MP_NO_ACTION;
14996 else if (element == EL_EXIT_OPEN ||
14997 element == EL_EM_EXIT_OPEN ||
14999 element == EL_EM_EXIT_OPENING ||
15001 element == EL_STEEL_EXIT_OPEN ||
15002 element == EL_EM_STEEL_EXIT_OPEN ||
15004 element == EL_EM_STEEL_EXIT_OPENING ||
15006 element == EL_SP_EXIT_OPEN ||
15007 element == EL_SP_EXIT_OPENING)
15009 sound_action = ACTION_PASSING; /* player is passing exit */
15011 else if (element == EL_EMPTY)
15013 sound_action = ACTION_MOVING; /* nothing to walk on */
15016 /* play sound from background or player, whatever is available */
15017 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15018 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15020 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15022 else if (player_can_move &&
15023 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15025 if (!ACCESS_FROM(element, opposite_direction))
15026 return MP_NO_ACTION; /* field not accessible from this direction */
15028 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15029 return MP_NO_ACTION;
15031 if (IS_EM_GATE(element))
15033 if (!player->key[EM_GATE_NR(element)])
15034 return MP_NO_ACTION;
15036 else if (IS_EM_GATE_GRAY(element))
15038 if (!player->key[EM_GATE_GRAY_NR(element)])
15039 return MP_NO_ACTION;
15041 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15043 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15044 return MP_NO_ACTION;
15046 else if (IS_EMC_GATE(element))
15048 if (!player->key[EMC_GATE_NR(element)])
15049 return MP_NO_ACTION;
15051 else if (IS_EMC_GATE_GRAY(element))
15053 if (!player->key[EMC_GATE_GRAY_NR(element)])
15054 return MP_NO_ACTION;
15056 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15058 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15059 return MP_NO_ACTION;
15061 else if (element == EL_DC_GATE_WHITE ||
15062 element == EL_DC_GATE_WHITE_GRAY ||
15063 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15065 if (player->num_white_keys == 0)
15066 return MP_NO_ACTION;
15068 player->num_white_keys--;
15070 else if (IS_SP_PORT(element))
15072 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15073 element == EL_SP_GRAVITY_PORT_RIGHT ||
15074 element == EL_SP_GRAVITY_PORT_UP ||
15075 element == EL_SP_GRAVITY_PORT_DOWN)
15076 #if USE_PLAYER_GRAVITY
15077 player->gravity = !player->gravity;
15079 game.gravity = !game.gravity;
15081 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15082 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15083 element == EL_SP_GRAVITY_ON_PORT_UP ||
15084 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15085 #if USE_PLAYER_GRAVITY
15086 player->gravity = TRUE;
15088 game.gravity = TRUE;
15090 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15091 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15092 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15093 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095 player->gravity = FALSE;
15097 game.gravity = FALSE;
15101 /* automatically move to the next field with double speed */
15102 player->programmed_action = move_direction;
15104 if (player->move_delay_reset_counter == 0)
15106 player->move_delay_reset_counter = 2; /* two double speed steps */
15108 DOUBLE_PLAYER_SPEED(player);
15111 PlayLevelSoundAction(x, y, ACTION_PASSING);
15113 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15117 if (mode != DF_SNAP)
15119 GfxElement[x][y] = GFX_ELEMENT(element);
15120 player->is_digging = TRUE;
15123 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15125 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15126 player->index_bit, dig_side);
15128 if (mode == DF_SNAP)
15130 #if USE_NEW_SNAP_DELAY
15131 if (level.block_snap_field)
15132 setFieldForSnapping(x, y, element, move_direction);
15134 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15136 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15139 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15140 player->index_bit, dig_side);
15143 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15147 if (is_player && mode != DF_SNAP)
15149 GfxElement[x][y] = element;
15150 player->is_collecting = TRUE;
15153 if (element == EL_SPEED_PILL)
15155 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15157 else if (element == EL_EXTRA_TIME && level.time > 0)
15159 TimeLeft += level.extra_time;
15162 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15164 DisplayGameControlValues();
15166 DrawGameValue_Time(TimeLeft);
15169 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15171 player->shield_normal_time_left += level.shield_normal_time;
15172 if (element == EL_SHIELD_DEADLY)
15173 player->shield_deadly_time_left += level.shield_deadly_time;
15175 else if (element == EL_DYNAMITE ||
15176 element == EL_EM_DYNAMITE ||
15177 element == EL_SP_DISK_RED)
15179 if (player->inventory_size < MAX_INVENTORY_SIZE)
15180 player->inventory_element[player->inventory_size++] = element;
15182 DrawGameDoorValues();
15184 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15186 player->dynabomb_count++;
15187 player->dynabombs_left++;
15189 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15191 player->dynabomb_size++;
15193 else if (element == EL_DYNABOMB_INCREASE_POWER)
15195 player->dynabomb_xl = TRUE;
15197 else if (IS_KEY(element))
15199 player->key[KEY_NR(element)] = TRUE;
15201 DrawGameDoorValues();
15203 else if (element == EL_DC_KEY_WHITE)
15205 player->num_white_keys++;
15207 /* display white keys? */
15208 /* DrawGameDoorValues(); */
15210 else if (IS_ENVELOPE(element))
15212 player->show_envelope = element;
15214 else if (element == EL_EMC_LENSES)
15216 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15218 RedrawAllInvisibleElementsForLenses();
15220 else if (element == EL_EMC_MAGNIFIER)
15222 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15224 RedrawAllInvisibleElementsForMagnifier();
15226 else if (IS_DROPPABLE(element) ||
15227 IS_THROWABLE(element)) /* can be collected and dropped */
15231 if (collect_count == 0)
15232 player->inventory_infinite_element = element;
15234 for (i = 0; i < collect_count; i++)
15235 if (player->inventory_size < MAX_INVENTORY_SIZE)
15236 player->inventory_element[player->inventory_size++] = element;
15238 DrawGameDoorValues();
15240 else if (collect_count > 0)
15242 local_player->gems_still_needed -= collect_count;
15243 if (local_player->gems_still_needed < 0)
15244 local_player->gems_still_needed = 0;
15247 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15249 DisplayGameControlValues();
15251 DrawGameValue_Emeralds(local_player->gems_still_needed);
15255 RaiseScoreElement(element);
15256 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15259 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15260 player->index_bit, dig_side);
15262 if (mode == DF_SNAP)
15264 #if USE_NEW_SNAP_DELAY
15265 if (level.block_snap_field)
15266 setFieldForSnapping(x, y, element, move_direction);
15268 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15270 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15273 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15274 player->index_bit, dig_side);
15277 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15279 if (mode == DF_SNAP && element != EL_BD_ROCK)
15280 return MP_NO_ACTION;
15282 if (CAN_FALL(element) && dy)
15283 return MP_NO_ACTION;
15285 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15286 !(element == EL_SPRING && level.use_spring_bug))
15287 return MP_NO_ACTION;
15289 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15290 ((move_direction & MV_VERTICAL &&
15291 ((element_info[element].move_pattern & MV_LEFT &&
15292 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15293 (element_info[element].move_pattern & MV_RIGHT &&
15294 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15295 (move_direction & MV_HORIZONTAL &&
15296 ((element_info[element].move_pattern & MV_UP &&
15297 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15298 (element_info[element].move_pattern & MV_DOWN &&
15299 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15300 return MP_NO_ACTION;
15302 /* do not push elements already moving away faster than player */
15303 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15304 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15305 return MP_NO_ACTION;
15307 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15309 if (player->push_delay_value == -1 || !player_was_pushing)
15310 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15312 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15314 if (player->push_delay_value == -1)
15315 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15317 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15319 if (!player->is_pushing)
15320 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15323 player->is_pushing = TRUE;
15324 player->is_active = TRUE;
15326 if (!(IN_LEV_FIELD(nextx, nexty) &&
15327 (IS_FREE(nextx, nexty) ||
15328 (IS_SB_ELEMENT(element) &&
15329 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15330 (IS_CUSTOM_ELEMENT(element) &&
15331 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15332 return MP_NO_ACTION;
15334 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15335 return MP_NO_ACTION;
15337 if (player->push_delay == -1) /* new pushing; restart delay */
15338 player->push_delay = 0;
15340 if (player->push_delay < player->push_delay_value &&
15341 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15342 element != EL_SPRING && element != EL_BALLOON)
15344 /* make sure that there is no move delay before next try to push */
15345 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15346 player->move_delay = 0;
15348 return MP_NO_ACTION;
15351 if (IS_CUSTOM_ELEMENT(element) &&
15352 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15354 if (!DigFieldByCE(nextx, nexty, element))
15355 return MP_NO_ACTION;
15358 if (IS_SB_ELEMENT(element))
15360 if (element == EL_SOKOBAN_FIELD_FULL)
15362 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15363 local_player->sokobanfields_still_needed++;
15366 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15368 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15369 local_player->sokobanfields_still_needed--;
15372 Feld[x][y] = EL_SOKOBAN_OBJECT;
15374 if (Back[x][y] == Back[nextx][nexty])
15375 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15376 else if (Back[x][y] != 0)
15377 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15380 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15383 if (local_player->sokobanfields_still_needed == 0 &&
15384 game.emulation == EMU_SOKOBAN)
15386 PlayerWins(player);
15388 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15392 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15394 InitMovingField(x, y, move_direction);
15395 GfxAction[x][y] = ACTION_PUSHING;
15397 if (mode == DF_SNAP)
15398 ContinueMoving(x, y);
15400 MovPos[x][y] = (dx != 0 ? dx : dy);
15402 Pushed[x][y] = TRUE;
15403 Pushed[nextx][nexty] = TRUE;
15405 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15406 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15408 player->push_delay_value = -1; /* get new value later */
15410 /* check for element change _after_ element has been pushed */
15411 if (game.use_change_when_pushing_bug)
15413 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15414 player->index_bit, dig_side);
15415 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15416 player->index_bit, dig_side);
15419 else if (IS_SWITCHABLE(element))
15421 if (PLAYER_SWITCHING(player, x, y))
15423 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15424 player->index_bit, dig_side);
15429 player->is_switching = TRUE;
15430 player->switch_x = x;
15431 player->switch_y = y;
15433 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15435 if (element == EL_ROBOT_WHEEL)
15437 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15441 game.robot_wheel_active = TRUE;
15443 TEST_DrawLevelField(x, y);
15445 else if (element == EL_SP_TERMINAL)
15449 SCAN_PLAYFIELD(xx, yy)
15451 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15453 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15454 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15457 else if (IS_BELT_SWITCH(element))
15459 ToggleBeltSwitch(x, y);
15461 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15462 element == EL_SWITCHGATE_SWITCH_DOWN ||
15463 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15464 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15466 ToggleSwitchgateSwitch(x, y);
15468 else if (element == EL_LIGHT_SWITCH ||
15469 element == EL_LIGHT_SWITCH_ACTIVE)
15471 ToggleLightSwitch(x, y);
15473 else if (element == EL_TIMEGATE_SWITCH ||
15474 element == EL_DC_TIMEGATE_SWITCH)
15476 ActivateTimegateSwitch(x, y);
15478 else if (element == EL_BALLOON_SWITCH_LEFT ||
15479 element == EL_BALLOON_SWITCH_RIGHT ||
15480 element == EL_BALLOON_SWITCH_UP ||
15481 element == EL_BALLOON_SWITCH_DOWN ||
15482 element == EL_BALLOON_SWITCH_NONE ||
15483 element == EL_BALLOON_SWITCH_ANY)
15485 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15486 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15487 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15488 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15489 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15492 else if (element == EL_LAMP)
15494 Feld[x][y] = EL_LAMP_ACTIVE;
15495 local_player->lights_still_needed--;
15497 ResetGfxAnimation(x, y);
15498 TEST_DrawLevelField(x, y);
15500 else if (element == EL_TIME_ORB_FULL)
15502 Feld[x][y] = EL_TIME_ORB_EMPTY;
15504 if (level.time > 0 || level.use_time_orb_bug)
15506 TimeLeft += level.time_orb_time;
15509 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15511 DisplayGameControlValues();
15513 DrawGameValue_Time(TimeLeft);
15517 ResetGfxAnimation(x, y);
15518 TEST_DrawLevelField(x, y);
15520 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15521 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15525 game.ball_state = !game.ball_state;
15527 SCAN_PLAYFIELD(xx, yy)
15529 int e = Feld[xx][yy];
15531 if (game.ball_state)
15533 if (e == EL_EMC_MAGIC_BALL)
15534 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15535 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15536 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15540 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15541 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15542 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15543 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15548 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15549 player->index_bit, dig_side);
15551 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15552 player->index_bit, dig_side);
15554 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15555 player->index_bit, dig_side);
15561 if (!PLAYER_SWITCHING(player, x, y))
15563 player->is_switching = TRUE;
15564 player->switch_x = x;
15565 player->switch_y = y;
15567 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15568 player->index_bit, dig_side);
15569 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15570 player->index_bit, dig_side);
15572 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15573 player->index_bit, dig_side);
15574 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15575 player->index_bit, dig_side);
15578 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15579 player->index_bit, dig_side);
15580 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15581 player->index_bit, dig_side);
15583 return MP_NO_ACTION;
15586 player->push_delay = -1;
15588 if (is_player) /* function can also be called by EL_PENGUIN */
15590 if (Feld[x][y] != element) /* really digged/collected something */
15592 player->is_collecting = !player->is_digging;
15593 player->is_active = TRUE;
15600 static boolean DigFieldByCE(int x, int y, int digging_element)
15602 int element = Feld[x][y];
15604 if (!IS_FREE(x, y))
15606 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15607 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15610 /* no element can dig solid indestructible elements */
15611 if (IS_INDESTRUCTIBLE(element) &&
15612 !IS_DIGGABLE(element) &&
15613 !IS_COLLECTIBLE(element))
15616 if (AmoebaNr[x][y] &&
15617 (element == EL_AMOEBA_FULL ||
15618 element == EL_BD_AMOEBA ||
15619 element == EL_AMOEBA_GROWING))
15621 AmoebaCnt[AmoebaNr[x][y]]--;
15622 AmoebaCnt2[AmoebaNr[x][y]]--;
15625 if (IS_MOVING(x, y))
15626 RemoveMovingField(x, y);
15630 TEST_DrawLevelField(x, y);
15633 /* if digged element was about to explode, prevent the explosion */
15634 ExplodeField[x][y] = EX_TYPE_NONE;
15636 PlayLevelSoundAction(x, y, action);
15639 Store[x][y] = EL_EMPTY;
15642 /* this makes it possible to leave the removed element again */
15643 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15644 Store[x][y] = element;
15646 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15648 int move_leave_element = element_info[digging_element].move_leave_element;
15650 /* this makes it possible to leave the removed element again */
15651 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15652 element : move_leave_element);
15659 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15661 int jx = player->jx, jy = player->jy;
15662 int x = jx + dx, y = jy + dy;
15663 int snap_direction = (dx == -1 ? MV_LEFT :
15664 dx == +1 ? MV_RIGHT :
15666 dy == +1 ? MV_DOWN : MV_NONE);
15667 boolean can_continue_snapping = (level.continuous_snapping &&
15668 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15670 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15673 if (!player->active || !IN_LEV_FIELD(x, y))
15681 if (player->MovPos == 0)
15682 player->is_pushing = FALSE;
15684 player->is_snapping = FALSE;
15686 if (player->MovPos == 0)
15688 player->is_moving = FALSE;
15689 player->is_digging = FALSE;
15690 player->is_collecting = FALSE;
15696 #if USE_NEW_CONTINUOUS_SNAPPING
15697 /* prevent snapping with already pressed snap key when not allowed */
15698 if (player->is_snapping && !can_continue_snapping)
15701 if (player->is_snapping)
15705 player->MovDir = snap_direction;
15707 if (player->MovPos == 0)
15709 player->is_moving = FALSE;
15710 player->is_digging = FALSE;
15711 player->is_collecting = FALSE;
15714 player->is_dropping = FALSE;
15715 player->is_dropping_pressed = FALSE;
15716 player->drop_pressed_delay = 0;
15718 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15721 player->is_snapping = TRUE;
15722 player->is_active = TRUE;
15724 if (player->MovPos == 0)
15726 player->is_moving = FALSE;
15727 player->is_digging = FALSE;
15728 player->is_collecting = FALSE;
15731 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15732 TEST_DrawLevelField(player->last_jx, player->last_jy);
15734 TEST_DrawLevelField(x, y);
15739 static boolean DropElement(struct PlayerInfo *player)
15741 int old_element, new_element;
15742 int dropx = player->jx, dropy = player->jy;
15743 int drop_direction = player->MovDir;
15744 int drop_side = drop_direction;
15746 int drop_element = get_next_dropped_element(player);
15748 int drop_element = (player->inventory_size > 0 ?
15749 player->inventory_element[player->inventory_size - 1] :
15750 player->inventory_infinite_element != EL_UNDEFINED ?
15751 player->inventory_infinite_element :
15752 player->dynabombs_left > 0 ?
15753 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15757 player->is_dropping_pressed = TRUE;
15759 /* do not drop an element on top of another element; when holding drop key
15760 pressed without moving, dropped element must move away before the next
15761 element can be dropped (this is especially important if the next element
15762 is dynamite, which can be placed on background for historical reasons) */
15763 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15766 if (IS_THROWABLE(drop_element))
15768 dropx += GET_DX_FROM_DIR(drop_direction);
15769 dropy += GET_DY_FROM_DIR(drop_direction);
15771 if (!IN_LEV_FIELD(dropx, dropy))
15775 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15776 new_element = drop_element; /* default: no change when dropping */
15778 /* check if player is active, not moving and ready to drop */
15779 if (!player->active || player->MovPos || player->drop_delay > 0)
15782 /* check if player has anything that can be dropped */
15783 if (new_element == EL_UNDEFINED)
15786 /* check if drop key was pressed long enough for EM style dynamite */
15787 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15790 /* check if anything can be dropped at the current position */
15791 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15794 /* collected custom elements can only be dropped on empty fields */
15795 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15798 if (old_element != EL_EMPTY)
15799 Back[dropx][dropy] = old_element; /* store old element on this field */
15801 ResetGfxAnimation(dropx, dropy);
15802 ResetRandomAnimationValue(dropx, dropy);
15804 if (player->inventory_size > 0 ||
15805 player->inventory_infinite_element != EL_UNDEFINED)
15807 if (player->inventory_size > 0)
15809 player->inventory_size--;
15811 DrawGameDoorValues();
15813 if (new_element == EL_DYNAMITE)
15814 new_element = EL_DYNAMITE_ACTIVE;
15815 else if (new_element == EL_EM_DYNAMITE)
15816 new_element = EL_EM_DYNAMITE_ACTIVE;
15817 else if (new_element == EL_SP_DISK_RED)
15818 new_element = EL_SP_DISK_RED_ACTIVE;
15821 Feld[dropx][dropy] = new_element;
15823 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15824 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15825 el2img(Feld[dropx][dropy]), 0);
15827 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15829 /* needed if previous element just changed to "empty" in the last frame */
15830 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15832 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15833 player->index_bit, drop_side);
15834 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15836 player->index_bit, drop_side);
15838 TestIfElementTouchesCustomElement(dropx, dropy);
15840 else /* player is dropping a dyna bomb */
15842 player->dynabombs_left--;
15844 Feld[dropx][dropy] = new_element;
15846 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15847 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15848 el2img(Feld[dropx][dropy]), 0);
15850 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15853 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15854 InitField_WithBug1(dropx, dropy, FALSE);
15856 new_element = Feld[dropx][dropy]; /* element might have changed */
15858 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15859 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15861 int move_direction, nextx, nexty;
15863 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15864 MovDir[dropx][dropy] = drop_direction;
15866 move_direction = MovDir[dropx][dropy];
15867 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15868 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15870 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15872 #if USE_FIX_IMPACT_COLLISION
15873 /* do not cause impact style collision by dropping elements that can fall */
15874 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15876 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15880 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15881 player->is_dropping = TRUE;
15883 player->drop_pressed_delay = 0;
15884 player->is_dropping_pressed = FALSE;
15886 player->drop_x = dropx;
15887 player->drop_y = dropy;
15892 /* ------------------------------------------------------------------------- */
15893 /* game sound playing functions */
15894 /* ------------------------------------------------------------------------- */
15896 static int *loop_sound_frame = NULL;
15897 static int *loop_sound_volume = NULL;
15899 void InitPlayLevelSound()
15901 int num_sounds = getSoundListSize();
15903 checked_free(loop_sound_frame);
15904 checked_free(loop_sound_volume);
15906 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15907 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15910 static void PlayLevelSound(int x, int y, int nr)
15912 int sx = SCREENX(x), sy = SCREENY(y);
15913 int volume, stereo_position;
15914 int max_distance = 8;
15915 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15917 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15918 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15921 if (!IN_LEV_FIELD(x, y) ||
15922 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15923 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15926 volume = SOUND_MAX_VOLUME;
15928 if (!IN_SCR_FIELD(sx, sy))
15930 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15931 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15933 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15936 stereo_position = (SOUND_MAX_LEFT +
15937 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15938 (SCR_FIELDX + 2 * max_distance));
15940 if (IS_LOOP_SOUND(nr))
15942 /* This assures that quieter loop sounds do not overwrite louder ones,
15943 while restarting sound volume comparison with each new game frame. */
15945 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15948 loop_sound_volume[nr] = volume;
15949 loop_sound_frame[nr] = FrameCounter;
15952 PlaySoundExt(nr, volume, stereo_position, type);
15955 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15957 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15958 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15959 y < LEVELY(BY1) ? LEVELY(BY1) :
15960 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15964 static void PlayLevelSoundAction(int x, int y, int action)
15966 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15969 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15971 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15973 if (sound_effect != SND_UNDEFINED)
15974 PlayLevelSound(x, y, sound_effect);
15977 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15980 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15982 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15983 PlayLevelSound(x, y, sound_effect);
15986 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15988 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15990 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15991 PlayLevelSound(x, y, sound_effect);
15994 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15996 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15998 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15999 StopSound(sound_effect);
16002 static void PlayLevelMusic()
16004 if (levelset.music[level_nr] != MUS_UNDEFINED)
16005 PlayMusic(levelset.music[level_nr]); /* from config file */
16007 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16010 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16012 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16013 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16014 int x = xx - 1 - offset;
16015 int y = yy - 1 - offset;
16020 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16024 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16028 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16032 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16036 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16040 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16044 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16047 case SAMPLE_android_clone:
16048 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16051 case SAMPLE_android_move:
16052 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16055 case SAMPLE_spring:
16056 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16060 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16064 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16067 case SAMPLE_eater_eat:
16068 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16072 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16075 case SAMPLE_collect:
16076 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16079 case SAMPLE_diamond:
16080 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16083 case SAMPLE_squash:
16084 /* !!! CHECK THIS !!! */
16086 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16088 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16092 case SAMPLE_wonderfall:
16093 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16097 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16101 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16105 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16109 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16113 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16117 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16120 case SAMPLE_wonder:
16121 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16125 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16128 case SAMPLE_exit_open:
16129 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16132 case SAMPLE_exit_leave:
16133 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16136 case SAMPLE_dynamite:
16137 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16141 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16145 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16149 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16153 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16157 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16161 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16165 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16171 void ChangeTime(int value)
16173 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16177 /* EMC game engine uses value from time counter of RND game engine */
16178 level.native_em_level->lev->time = *time;
16180 DrawGameValue_Time(*time);
16183 void RaiseScore(int value)
16185 /* EMC game engine and RND game engine have separate score counters */
16186 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16187 &level.native_em_level->lev->score : &local_player->score);
16191 DrawGameValue_Score(*score);
16195 void RaiseScore(int value)
16197 local_player->score += value;
16200 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16202 DisplayGameControlValues();
16204 DrawGameValue_Score(local_player->score);
16208 void RaiseScoreElement(int element)
16213 case EL_BD_DIAMOND:
16214 case EL_EMERALD_YELLOW:
16215 case EL_EMERALD_RED:
16216 case EL_EMERALD_PURPLE:
16217 case EL_SP_INFOTRON:
16218 RaiseScore(level.score[SC_EMERALD]);
16221 RaiseScore(level.score[SC_DIAMOND]);
16224 RaiseScore(level.score[SC_CRYSTAL]);
16227 RaiseScore(level.score[SC_PEARL]);
16230 case EL_BD_BUTTERFLY:
16231 case EL_SP_ELECTRON:
16232 RaiseScore(level.score[SC_BUG]);
16235 case EL_BD_FIREFLY:
16236 case EL_SP_SNIKSNAK:
16237 RaiseScore(level.score[SC_SPACESHIP]);
16240 case EL_DARK_YAMYAM:
16241 RaiseScore(level.score[SC_YAMYAM]);
16244 RaiseScore(level.score[SC_ROBOT]);
16247 RaiseScore(level.score[SC_PACMAN]);
16250 RaiseScore(level.score[SC_NUT]);
16253 case EL_EM_DYNAMITE:
16254 case EL_SP_DISK_RED:
16255 case EL_DYNABOMB_INCREASE_NUMBER:
16256 case EL_DYNABOMB_INCREASE_SIZE:
16257 case EL_DYNABOMB_INCREASE_POWER:
16258 RaiseScore(level.score[SC_DYNAMITE]);
16260 case EL_SHIELD_NORMAL:
16261 case EL_SHIELD_DEADLY:
16262 RaiseScore(level.score[SC_SHIELD]);
16264 case EL_EXTRA_TIME:
16265 RaiseScore(level.extra_time_score);
16279 case EL_DC_KEY_WHITE:
16280 RaiseScore(level.score[SC_KEY]);
16283 RaiseScore(element_info[element].collect_score);
16288 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16290 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16292 #if defined(NETWORK_AVALIABLE)
16293 if (options.network)
16294 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16303 FadeSkipNextFadeIn();
16305 fading = fading_none;
16309 OpenDoor(DOOR_CLOSE_1);
16312 game_status = GAME_MODE_MAIN;
16315 DrawAndFadeInMainMenu(REDRAW_FIELD);
16323 FadeOut(REDRAW_FIELD);
16326 game_status = GAME_MODE_MAIN;
16328 DrawAndFadeInMainMenu(REDRAW_FIELD);
16332 else /* continue playing the game */
16334 if (tape.playing && tape.deactivate_display)
16335 TapeDeactivateDisplayOff(TRUE);
16337 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16339 if (tape.playing && tape.deactivate_display)
16340 TapeDeactivateDisplayOn();
16344 void RequestQuitGame(boolean ask_if_really_quit)
16346 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16347 boolean skip_request = AllPlayersGone || quick_quit;
16349 RequestQuitGameExt(skip_request, quick_quit,
16350 "Do you really want to quit the game ?");
16354 /* ------------------------------------------------------------------------- */
16355 /* random generator functions */
16356 /* ------------------------------------------------------------------------- */
16358 unsigned int InitEngineRandom_RND(long seed)
16360 game.num_random_calls = 0;
16363 unsigned int rnd_seed = InitEngineRandom(seed);
16365 printf("::: START RND: %d\n", rnd_seed);
16370 return InitEngineRandom(seed);
16376 unsigned int RND(int max)
16380 game.num_random_calls++;
16382 return GetEngineRandom(max);
16389 /* ------------------------------------------------------------------------- */
16390 /* game engine snapshot handling functions */
16391 /* ------------------------------------------------------------------------- */
16393 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16395 struct EngineSnapshotInfo
16397 /* runtime values for custom element collect score */
16398 int collect_score[NUM_CUSTOM_ELEMENTS];
16400 /* runtime values for group element choice position */
16401 int choice_pos[NUM_GROUP_ELEMENTS];
16403 /* runtime values for belt position animations */
16404 int belt_graphic[4 * NUM_BELT_PARTS];
16405 int belt_anim_mode[4 * NUM_BELT_PARTS];
16408 struct EngineSnapshotNodeInfo
16415 static struct EngineSnapshotInfo engine_snapshot_rnd;
16416 static ListNode *engine_snapshot_list = NULL;
16417 static char *snapshot_level_identifier = NULL;
16418 static int snapshot_level_nr = -1;
16420 void FreeEngineSnapshot()
16422 while (engine_snapshot_list != NULL)
16423 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16426 setString(&snapshot_level_identifier, NULL);
16427 snapshot_level_nr = -1;
16430 static void SaveEngineSnapshotValues_RND()
16432 static int belt_base_active_element[4] =
16434 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16435 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16436 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16437 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16441 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16443 int element = EL_CUSTOM_START + i;
16445 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16448 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16450 int element = EL_GROUP_START + i;
16452 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16455 for (i = 0; i < 4; i++)
16457 for (j = 0; j < NUM_BELT_PARTS; j++)
16459 int element = belt_base_active_element[i] + j;
16460 int graphic = el2img(element);
16461 int anim_mode = graphic_info[graphic].anim_mode;
16463 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16464 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16469 static void LoadEngineSnapshotValues_RND()
16471 unsigned long num_random_calls = game.num_random_calls;
16474 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16476 int element = EL_CUSTOM_START + i;
16478 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16481 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16483 int element = EL_GROUP_START + i;
16485 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16488 for (i = 0; i < 4; i++)
16490 for (j = 0; j < NUM_BELT_PARTS; j++)
16492 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16493 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16495 graphic_info[graphic].anim_mode = anim_mode;
16499 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16501 InitRND(tape.random_seed);
16502 for (i = 0; i < num_random_calls; i++)
16506 if (game.num_random_calls != num_random_calls)
16508 Error(ERR_INFO, "number of random calls out of sync");
16509 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16510 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16511 Error(ERR_EXIT, "this should not happen -- please debug");
16515 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16517 struct EngineSnapshotNodeInfo *bi =
16518 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16520 bi->buffer_orig = buffer;
16521 bi->buffer_copy = checked_malloc(size);
16524 memcpy(bi->buffer_copy, buffer, size);
16526 addNodeToList(&engine_snapshot_list, NULL, bi);
16529 void SaveEngineSnapshot()
16531 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16533 if (level_editor_test_game) /* do not save snapshots from editor */
16536 /* copy some special values to a structure better suited for the snapshot */
16538 SaveEngineSnapshotValues_RND();
16539 SaveEngineSnapshotValues_EM();
16541 /* save values stored in special snapshot structure */
16543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16546 /* save further RND engine values */
16548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16585 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16588 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16601 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16604 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16605 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16606 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16607 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16609 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16610 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16612 /* save level identification information */
16614 setString(&snapshot_level_identifier, leveldir_current->identifier);
16615 snapshot_level_nr = level_nr;
16618 ListNode *node = engine_snapshot_list;
16621 while (node != NULL)
16623 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16628 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16632 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16634 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16637 void LoadEngineSnapshot()
16639 ListNode *node = engine_snapshot_list;
16641 if (engine_snapshot_list == NULL)
16644 while (node != NULL)
16646 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16651 /* restore special values from snapshot structure */
16653 LoadEngineSnapshotValues_RND();
16654 LoadEngineSnapshotValues_EM();
16657 boolean CheckEngineSnapshot()
16659 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16660 snapshot_level_nr == level_nr);
16664 /* ---------- new game button stuff ---------------------------------------- */
16666 /* graphic position values for game buttons */
16667 #define GAME_BUTTON_XSIZE 30
16668 #define GAME_BUTTON_YSIZE 30
16669 #define GAME_BUTTON_XPOS 5
16670 #define GAME_BUTTON_YPOS 215
16671 #define SOUND_BUTTON_XPOS 5
16672 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16674 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16675 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16676 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16677 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16678 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16679 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16687 } gamebutton_info[NUM_GAME_BUTTONS] =
16691 &game.button.stop.x, &game.button.stop.y,
16692 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16697 &game.button.pause.x, &game.button.pause.y,
16698 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16699 GAME_CTRL_ID_PAUSE,
16703 &game.button.play.x, &game.button.play.y,
16704 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16709 &game.button.sound_music.x, &game.button.sound_music.y,
16710 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16711 SOUND_CTRL_ID_MUSIC,
16712 "background music on/off"
16715 &game.button.sound_loops.x, &game.button.sound_loops.y,
16716 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16717 SOUND_CTRL_ID_LOOPS,
16718 "sound loops on/off"
16721 &game.button.sound_simple.x,&game.button.sound_simple.y,
16722 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16723 SOUND_CTRL_ID_SIMPLE,
16724 "normal sounds on/off"
16728 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16733 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16734 GAME_CTRL_ID_PAUSE,
16738 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16743 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16744 SOUND_CTRL_ID_MUSIC,
16745 "background music on/off"
16748 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16749 SOUND_CTRL_ID_LOOPS,
16750 "sound loops on/off"
16753 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16754 SOUND_CTRL_ID_SIMPLE,
16755 "normal sounds on/off"
16760 void CreateGameButtons()
16764 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16766 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16767 struct GadgetInfo *gi;
16770 unsigned long event_mask;
16772 int gd_xoffset, gd_yoffset;
16773 int gd_x1, gd_x2, gd_y1, gd_y2;
16776 x = DX + *gamebutton_info[i].x;
16777 y = DY + *gamebutton_info[i].y;
16778 gd_xoffset = gamebutton_info[i].gd_x;
16779 gd_yoffset = gamebutton_info[i].gd_y;
16780 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16781 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16783 if (id == GAME_CTRL_ID_STOP ||
16784 id == GAME_CTRL_ID_PAUSE ||
16785 id == GAME_CTRL_ID_PLAY)
16787 button_type = GD_TYPE_NORMAL_BUTTON;
16789 event_mask = GD_EVENT_RELEASED;
16790 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16791 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16795 button_type = GD_TYPE_CHECK_BUTTON;
16797 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16798 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16799 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16800 event_mask = GD_EVENT_PRESSED;
16801 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16802 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16805 gi = CreateGadget(GDI_CUSTOM_ID, id,
16806 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16811 GDI_X, DX + gd_xoffset,
16812 GDI_Y, DY + gd_yoffset,
16814 GDI_WIDTH, GAME_BUTTON_XSIZE,
16815 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16816 GDI_TYPE, button_type,
16817 GDI_STATE, GD_BUTTON_UNPRESSED,
16818 GDI_CHECKED, checked,
16819 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16820 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16821 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16822 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16823 GDI_DIRECT_DRAW, FALSE,
16824 GDI_EVENT_MASK, event_mask,
16825 GDI_CALLBACK_ACTION, HandleGameButtons,
16829 Error(ERR_EXIT, "cannot create gadget");
16831 game_gadget[id] = gi;
16835 void FreeGameButtons()
16839 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16840 FreeGadget(game_gadget[i]);
16843 static void MapGameButtons()
16847 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16848 MapGadget(game_gadget[i]);
16851 void UnmapGameButtons()
16855 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16856 UnmapGadget(game_gadget[i]);
16859 void RedrawGameButtons()
16863 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16864 RedrawGadget(game_gadget[i]);
16867 static void HandleGameButtons(struct GadgetInfo *gi)
16869 int id = gi->custom_id;
16871 if (game_status != GAME_MODE_PLAYING)
16876 case GAME_CTRL_ID_STOP:
16880 RequestQuitGame(TRUE);
16883 case GAME_CTRL_ID_PAUSE:
16884 if (options.network)
16886 #if defined(NETWORK_AVALIABLE)
16888 SendToServer_ContinuePlaying();
16890 SendToServer_PausePlaying();
16894 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16897 case GAME_CTRL_ID_PLAY:
16900 #if defined(NETWORK_AVALIABLE)
16901 if (options.network)
16902 SendToServer_ContinuePlaying();
16906 tape.pausing = FALSE;
16907 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16912 case SOUND_CTRL_ID_MUSIC:
16913 if (setup.sound_music)
16915 setup.sound_music = FALSE;
16918 else if (audio.music_available)
16920 setup.sound = setup.sound_music = TRUE;
16922 SetAudioMode(setup.sound);
16928 case SOUND_CTRL_ID_LOOPS:
16929 if (setup.sound_loops)
16930 setup.sound_loops = FALSE;
16931 else if (audio.loops_available)
16933 setup.sound = setup.sound_loops = TRUE;
16934 SetAudioMode(setup.sound);
16938 case SOUND_CTRL_ID_SIMPLE:
16939 if (setup.sound_simple)
16940 setup.sound_simple = FALSE;
16941 else if (audio.sound_available)
16943 setup.sound = setup.sound_simple = TRUE;
16944 SetAudioMode(setup.sound);