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 /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5776 possible that the relocation target field did not contain a player element,
5777 but a walkable element, to which the new player was relocated -- in this
5778 case, restore that (already initialized!) element on the player field */
5779 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5781 Feld[jx][jy] = element; /* restore previously existing element */
5783 /* !!! do not initialize already initialized element a second time !!! */
5784 /* (this causes at least problems with "element creation" CE trigger for
5785 already existing elements, and existing Sokoban fields counted twice) */
5786 InitField(jx, jy, FALSE);
5790 /* only visually relocate centered player */
5791 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5792 FALSE, level.instant_relocation);
5794 TestIfPlayerTouchesBadThing(jx, jy);
5795 TestIfPlayerTouchesCustomElement(jx, jy);
5797 if (IS_CUSTOM_ELEMENT(element))
5798 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5799 player->index_bit, enter_side);
5801 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5802 player->index_bit, enter_side);
5805 if (player->is_switching)
5807 /* ensure that relocation while still switching an element does not cause
5808 a new element to be treated as also switched directly after relocation
5809 (this is important for teleporter switches that teleport the player to
5810 a place where another teleporter switch is in the same direction, which
5811 would then incorrectly be treated as immediately switched before the
5812 direction key that caused the switch was released) */
5814 player->switch_x += jx - old_jx;
5815 player->switch_y += jy - old_jy;
5820 void Explode(int ex, int ey, int phase, int mode)
5826 /* !!! eliminate this variable !!! */
5827 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5829 if (game.explosions_delayed)
5831 ExplodeField[ex][ey] = mode;
5835 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5837 int center_element = Feld[ex][ey];
5838 int artwork_element, explosion_element; /* set these values later */
5841 /* --- This is only really needed (and now handled) in "Impact()". --- */
5842 /* do not explode moving elements that left the explode field in time */
5843 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5844 center_element == EL_EMPTY &&
5845 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5850 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5851 if (mode == EX_TYPE_NORMAL ||
5852 mode == EX_TYPE_CENTER ||
5853 mode == EX_TYPE_CROSS)
5854 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5857 /* remove things displayed in background while burning dynamite */
5858 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5861 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5863 /* put moving element to center field (and let it explode there) */
5864 center_element = MovingOrBlocked2Element(ex, ey);
5865 RemoveMovingField(ex, ey);
5866 Feld[ex][ey] = center_element;
5869 /* now "center_element" is finally determined -- set related values now */
5870 artwork_element = center_element; /* for custom player artwork */
5871 explosion_element = center_element; /* for custom player artwork */
5873 if (IS_PLAYER(ex, ey))
5875 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5877 artwork_element = stored_player[player_nr].artwork_element;
5879 if (level.use_explosion_element[player_nr])
5881 explosion_element = level.explosion_element[player_nr];
5882 artwork_element = explosion_element;
5887 if (mode == EX_TYPE_NORMAL ||
5888 mode == EX_TYPE_CENTER ||
5889 mode == EX_TYPE_CROSS)
5890 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5893 last_phase = element_info[explosion_element].explosion_delay + 1;
5895 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5897 int xx = x - ex + 1;
5898 int yy = y - ey + 1;
5901 if (!IN_LEV_FIELD(x, y) ||
5902 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5903 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5906 element = Feld[x][y];
5908 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5910 element = MovingOrBlocked2Element(x, y);
5912 if (!IS_EXPLOSION_PROOF(element))
5913 RemoveMovingField(x, y);
5916 /* indestructible elements can only explode in center (but not flames) */
5917 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5918 mode == EX_TYPE_BORDER)) ||
5919 element == EL_FLAMES)
5922 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5923 behaviour, for example when touching a yamyam that explodes to rocks
5924 with active deadly shield, a rock is created under the player !!! */
5925 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5927 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5928 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5929 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5931 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5934 if (IS_ACTIVE_BOMB(element))
5936 /* re-activate things under the bomb like gate or penguin */
5937 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5944 /* save walkable background elements while explosion on same tile */
5945 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5946 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5947 Back[x][y] = element;
5949 /* ignite explodable elements reached by other explosion */
5950 if (element == EL_EXPLOSION)
5951 element = Store2[x][y];
5953 if (AmoebaNr[x][y] &&
5954 (element == EL_AMOEBA_FULL ||
5955 element == EL_BD_AMOEBA ||
5956 element == EL_AMOEBA_GROWING))
5958 AmoebaCnt[AmoebaNr[x][y]]--;
5959 AmoebaCnt2[AmoebaNr[x][y]]--;
5964 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5966 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5968 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5970 if (PLAYERINFO(ex, ey)->use_murphy)
5971 Store[x][y] = EL_EMPTY;
5974 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5975 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5976 else if (ELEM_IS_PLAYER(center_element))
5977 Store[x][y] = EL_EMPTY;
5978 else if (center_element == EL_YAMYAM)
5979 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5980 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5981 Store[x][y] = element_info[center_element].content.e[xx][yy];
5983 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5984 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5985 otherwise) -- FIX THIS !!! */
5986 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5987 Store[x][y] = element_info[element].content.e[1][1];
5989 else if (!CAN_EXPLODE(element))
5990 Store[x][y] = element_info[element].content.e[1][1];
5993 Store[x][y] = EL_EMPTY;
5995 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5996 center_element == EL_AMOEBA_TO_DIAMOND)
5997 Store2[x][y] = element;
5999 Feld[x][y] = EL_EXPLOSION;
6000 GfxElement[x][y] = artwork_element;
6002 ExplodePhase[x][y] = 1;
6003 ExplodeDelay[x][y] = last_phase;
6008 if (center_element == EL_YAMYAM)
6009 game.yamyam_content_nr =
6010 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6022 GfxFrame[x][y] = 0; /* restart explosion animation */
6024 last_phase = ExplodeDelay[x][y];
6026 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6030 /* activate this even in non-DEBUG version until cause for crash in
6031 getGraphicAnimationFrame() (see below) is found and eliminated */
6037 /* this can happen if the player leaves an explosion just in time */
6038 if (GfxElement[x][y] == EL_UNDEFINED)
6039 GfxElement[x][y] = EL_EMPTY;
6041 if (GfxElement[x][y] == EL_UNDEFINED)
6044 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6045 printf("Explode(): This should never happen!\n");
6048 GfxElement[x][y] = EL_EMPTY;
6054 border_element = Store2[x][y];
6055 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6056 border_element = StorePlayer[x][y];
6058 if (phase == element_info[border_element].ignition_delay ||
6059 phase == last_phase)
6061 boolean border_explosion = FALSE;
6063 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6064 !PLAYER_EXPLOSION_PROTECTED(x, y))
6066 KillPlayerUnlessExplosionProtected(x, y);
6067 border_explosion = TRUE;
6069 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6071 Feld[x][y] = Store2[x][y];
6074 border_explosion = TRUE;
6076 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6078 AmoebeUmwandeln(x, y);
6080 border_explosion = TRUE;
6083 /* if an element just explodes due to another explosion (chain-reaction),
6084 do not immediately end the new explosion when it was the last frame of
6085 the explosion (as it would be done in the following "if"-statement!) */
6086 if (border_explosion && phase == last_phase)
6090 if (phase == last_phase)
6094 element = Feld[x][y] = Store[x][y];
6095 Store[x][y] = Store2[x][y] = 0;
6096 GfxElement[x][y] = EL_UNDEFINED;
6098 /* player can escape from explosions and might therefore be still alive */
6099 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6100 element <= EL_PLAYER_IS_EXPLODING_4)
6102 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6103 int explosion_element = EL_PLAYER_1 + player_nr;
6104 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6105 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6107 if (level.use_explosion_element[player_nr])
6108 explosion_element = level.explosion_element[player_nr];
6110 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6111 element_info[explosion_element].content.e[xx][yy]);
6114 /* restore probably existing indestructible background element */
6115 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6116 element = Feld[x][y] = Back[x][y];
6119 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6120 GfxDir[x][y] = MV_NONE;
6121 ChangeDelay[x][y] = 0;
6122 ChangePage[x][y] = -1;
6124 #if USE_NEW_CUSTOM_VALUE
6125 CustomValue[x][y] = 0;
6128 InitField_WithBug2(x, y, FALSE);
6130 TEST_DrawLevelField(x, y);
6132 TestIfElementTouchesCustomElement(x, y);
6134 if (GFX_CRUMBLED(element))
6135 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6137 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6138 StorePlayer[x][y] = 0;
6140 if (ELEM_IS_PLAYER(element))
6141 RelocatePlayer(x, y, element);
6143 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6145 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6146 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6149 TEST_DrawLevelFieldCrumbledSand(x, y);
6151 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6153 DrawLevelElement(x, y, Back[x][y]);
6154 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6156 else if (IS_WALKABLE_UNDER(Back[x][y]))
6158 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6159 DrawLevelElementThruMask(x, y, Back[x][y]);
6161 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6162 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6166 void DynaExplode(int ex, int ey)
6169 int dynabomb_element = Feld[ex][ey];
6170 int dynabomb_size = 1;
6171 boolean dynabomb_xl = FALSE;
6172 struct PlayerInfo *player;
6173 static int xy[4][2] =
6181 if (IS_ACTIVE_BOMB(dynabomb_element))
6183 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6184 dynabomb_size = player->dynabomb_size;
6185 dynabomb_xl = player->dynabomb_xl;
6186 player->dynabombs_left++;
6189 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6191 for (i = 0; i < NUM_DIRECTIONS; i++)
6193 for (j = 1; j <= dynabomb_size; j++)
6195 int x = ex + j * xy[i][0];
6196 int y = ey + j * xy[i][1];
6199 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6202 element = Feld[x][y];
6204 /* do not restart explosions of fields with active bombs */
6205 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6208 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6210 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6211 !IS_DIGGABLE(element) && !dynabomb_xl)
6217 void Bang(int x, int y)
6219 int element = MovingOrBlocked2Element(x, y);
6220 int explosion_type = EX_TYPE_NORMAL;
6222 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6224 struct PlayerInfo *player = PLAYERINFO(x, y);
6226 #if USE_FIX_CE_ACTION_WITH_PLAYER
6227 element = Feld[x][y] = player->initial_element;
6229 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6230 player->element_nr);
6233 if (level.use_explosion_element[player->index_nr])
6235 int explosion_element = level.explosion_element[player->index_nr];
6237 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6238 explosion_type = EX_TYPE_CROSS;
6239 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6240 explosion_type = EX_TYPE_CENTER;
6248 case EL_BD_BUTTERFLY:
6251 case EL_DARK_YAMYAM:
6255 RaiseScoreElement(element);
6258 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6259 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6260 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6261 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6262 case EL_DYNABOMB_INCREASE_NUMBER:
6263 case EL_DYNABOMB_INCREASE_SIZE:
6264 case EL_DYNABOMB_INCREASE_POWER:
6265 explosion_type = EX_TYPE_DYNA;
6268 case EL_DC_LANDMINE:
6270 case EL_EM_EXIT_OPEN:
6271 case EL_EM_STEEL_EXIT_OPEN:
6273 explosion_type = EX_TYPE_CENTER;
6278 case EL_LAMP_ACTIVE:
6279 case EL_AMOEBA_TO_DIAMOND:
6280 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6281 explosion_type = EX_TYPE_CENTER;
6285 if (element_info[element].explosion_type == EXPLODES_CROSS)
6286 explosion_type = EX_TYPE_CROSS;
6287 else if (element_info[element].explosion_type == EXPLODES_1X1)
6288 explosion_type = EX_TYPE_CENTER;
6292 if (explosion_type == EX_TYPE_DYNA)
6295 Explode(x, y, EX_PHASE_START, explosion_type);
6297 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6300 void SplashAcid(int x, int y)
6302 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6303 (!IN_LEV_FIELD(x - 1, y - 2) ||
6304 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6305 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6307 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6308 (!IN_LEV_FIELD(x + 1, y - 2) ||
6309 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6310 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6312 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6315 static void InitBeltMovement()
6317 static int belt_base_element[4] =
6319 EL_CONVEYOR_BELT_1_LEFT,
6320 EL_CONVEYOR_BELT_2_LEFT,
6321 EL_CONVEYOR_BELT_3_LEFT,
6322 EL_CONVEYOR_BELT_4_LEFT
6324 static int belt_base_active_element[4] =
6326 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6327 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6328 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6329 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6334 /* set frame order for belt animation graphic according to belt direction */
6335 for (i = 0; i < NUM_BELTS; i++)
6339 for (j = 0; j < NUM_BELT_PARTS; j++)
6341 int element = belt_base_active_element[belt_nr] + j;
6342 int graphic_1 = el2img(element);
6343 int graphic_2 = el2panelimg(element);
6345 if (game.belt_dir[i] == MV_LEFT)
6347 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6348 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6352 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6353 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6358 SCAN_PLAYFIELD(x, y)
6360 int element = Feld[x][y];
6362 for (i = 0; i < NUM_BELTS; i++)
6364 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6366 int e_belt_nr = getBeltNrFromBeltElement(element);
6369 if (e_belt_nr == belt_nr)
6371 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6373 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6380 static void ToggleBeltSwitch(int x, int y)
6382 static int belt_base_element[4] =
6384 EL_CONVEYOR_BELT_1_LEFT,
6385 EL_CONVEYOR_BELT_2_LEFT,
6386 EL_CONVEYOR_BELT_3_LEFT,
6387 EL_CONVEYOR_BELT_4_LEFT
6389 static int belt_base_active_element[4] =
6391 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6392 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6393 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6394 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6396 static int belt_base_switch_element[4] =
6398 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6399 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6400 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6401 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6403 static int belt_move_dir[4] =
6411 int element = Feld[x][y];
6412 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6413 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6414 int belt_dir = belt_move_dir[belt_dir_nr];
6417 if (!IS_BELT_SWITCH(element))
6420 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6421 game.belt_dir[belt_nr] = belt_dir;
6423 if (belt_dir_nr == 3)
6426 /* set frame order for belt animation graphic according to belt direction */
6427 for (i = 0; i < NUM_BELT_PARTS; i++)
6429 int element = belt_base_active_element[belt_nr] + i;
6430 int graphic_1 = el2img(element);
6431 int graphic_2 = el2panelimg(element);
6433 if (belt_dir == MV_LEFT)
6435 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6436 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6440 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6441 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6445 SCAN_PLAYFIELD(xx, yy)
6447 int element = Feld[xx][yy];
6449 if (IS_BELT_SWITCH(element))
6451 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6453 if (e_belt_nr == belt_nr)
6455 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6456 TEST_DrawLevelField(xx, yy);
6459 else if (IS_BELT(element) && belt_dir != MV_NONE)
6461 int e_belt_nr = getBeltNrFromBeltElement(element);
6463 if (e_belt_nr == belt_nr)
6465 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6467 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6468 TEST_DrawLevelField(xx, yy);
6471 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6473 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6475 if (e_belt_nr == belt_nr)
6477 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6479 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6480 TEST_DrawLevelField(xx, yy);
6486 static void ToggleSwitchgateSwitch(int x, int y)
6490 game.switchgate_pos = !game.switchgate_pos;
6492 SCAN_PLAYFIELD(xx, yy)
6494 int element = Feld[xx][yy];
6496 #if !USE_BOTH_SWITCHGATE_SWITCHES
6497 if (element == EL_SWITCHGATE_SWITCH_UP ||
6498 element == EL_SWITCHGATE_SWITCH_DOWN)
6500 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6501 TEST_DrawLevelField(xx, yy);
6503 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6504 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6506 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6507 TEST_DrawLevelField(xx, yy);
6510 if (element == EL_SWITCHGATE_SWITCH_UP)
6512 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6513 TEST_DrawLevelField(xx, yy);
6515 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6517 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6518 TEST_DrawLevelField(xx, yy);
6520 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6522 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6523 TEST_DrawLevelField(xx, yy);
6525 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6527 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6528 TEST_DrawLevelField(xx, yy);
6531 else if (element == EL_SWITCHGATE_OPEN ||
6532 element == EL_SWITCHGATE_OPENING)
6534 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6536 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6538 else if (element == EL_SWITCHGATE_CLOSED ||
6539 element == EL_SWITCHGATE_CLOSING)
6541 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6543 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6548 static int getInvisibleActiveFromInvisibleElement(int element)
6550 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6551 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6552 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6556 static int getInvisibleFromInvisibleActiveElement(int element)
6558 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6559 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6560 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6564 static void RedrawAllLightSwitchesAndInvisibleElements()
6568 SCAN_PLAYFIELD(x, y)
6570 int element = Feld[x][y];
6572 if (element == EL_LIGHT_SWITCH &&
6573 game.light_time_left > 0)
6575 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6576 TEST_DrawLevelField(x, y);
6578 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6579 game.light_time_left == 0)
6581 Feld[x][y] = EL_LIGHT_SWITCH;
6582 TEST_DrawLevelField(x, y);
6584 else if (element == EL_EMC_DRIPPER &&
6585 game.light_time_left > 0)
6587 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6588 TEST_DrawLevelField(x, y);
6590 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6591 game.light_time_left == 0)
6593 Feld[x][y] = EL_EMC_DRIPPER;
6594 TEST_DrawLevelField(x, y);
6596 else if (element == EL_INVISIBLE_STEELWALL ||
6597 element == EL_INVISIBLE_WALL ||
6598 element == EL_INVISIBLE_SAND)
6600 if (game.light_time_left > 0)
6601 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6603 TEST_DrawLevelField(x, y);
6605 /* uncrumble neighbour fields, if needed */
6606 if (element == EL_INVISIBLE_SAND)
6607 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6609 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6610 element == EL_INVISIBLE_WALL_ACTIVE ||
6611 element == EL_INVISIBLE_SAND_ACTIVE)
6613 if (game.light_time_left == 0)
6614 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6616 TEST_DrawLevelField(x, y);
6618 /* re-crumble neighbour fields, if needed */
6619 if (element == EL_INVISIBLE_SAND)
6620 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6625 static void RedrawAllInvisibleElementsForLenses()
6629 SCAN_PLAYFIELD(x, y)
6631 int element = Feld[x][y];
6633 if (element == EL_EMC_DRIPPER &&
6634 game.lenses_time_left > 0)
6636 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6637 TEST_DrawLevelField(x, y);
6639 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6640 game.lenses_time_left == 0)
6642 Feld[x][y] = EL_EMC_DRIPPER;
6643 TEST_DrawLevelField(x, y);
6645 else if (element == EL_INVISIBLE_STEELWALL ||
6646 element == EL_INVISIBLE_WALL ||
6647 element == EL_INVISIBLE_SAND)
6649 if (game.lenses_time_left > 0)
6650 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6652 TEST_DrawLevelField(x, y);
6654 /* uncrumble neighbour fields, if needed */
6655 if (element == EL_INVISIBLE_SAND)
6656 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6658 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6659 element == EL_INVISIBLE_WALL_ACTIVE ||
6660 element == EL_INVISIBLE_SAND_ACTIVE)
6662 if (game.lenses_time_left == 0)
6663 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6665 TEST_DrawLevelField(x, y);
6667 /* re-crumble neighbour fields, if needed */
6668 if (element == EL_INVISIBLE_SAND)
6669 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6674 static void RedrawAllInvisibleElementsForMagnifier()
6678 SCAN_PLAYFIELD(x, y)
6680 int element = Feld[x][y];
6682 if (element == EL_EMC_FAKE_GRASS &&
6683 game.magnify_time_left > 0)
6685 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6686 TEST_DrawLevelField(x, y);
6688 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6689 game.magnify_time_left == 0)
6691 Feld[x][y] = EL_EMC_FAKE_GRASS;
6692 TEST_DrawLevelField(x, y);
6694 else if (IS_GATE_GRAY(element) &&
6695 game.magnify_time_left > 0)
6697 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6698 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6699 IS_EM_GATE_GRAY(element) ?
6700 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6701 IS_EMC_GATE_GRAY(element) ?
6702 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6703 IS_DC_GATE_GRAY(element) ?
6704 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6706 TEST_DrawLevelField(x, y);
6708 else if (IS_GATE_GRAY_ACTIVE(element) &&
6709 game.magnify_time_left == 0)
6711 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6712 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6713 IS_EM_GATE_GRAY_ACTIVE(element) ?
6714 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6715 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6716 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6717 IS_DC_GATE_GRAY_ACTIVE(element) ?
6718 EL_DC_GATE_WHITE_GRAY :
6720 TEST_DrawLevelField(x, y);
6725 static void ToggleLightSwitch(int x, int y)
6727 int element = Feld[x][y];
6729 game.light_time_left =
6730 (element == EL_LIGHT_SWITCH ?
6731 level.time_light * FRAMES_PER_SECOND : 0);
6733 RedrawAllLightSwitchesAndInvisibleElements();
6736 static void ActivateTimegateSwitch(int x, int y)
6740 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6742 SCAN_PLAYFIELD(xx, yy)
6744 int element = Feld[xx][yy];
6746 if (element == EL_TIMEGATE_CLOSED ||
6747 element == EL_TIMEGATE_CLOSING)
6749 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6750 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6754 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6756 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6757 TEST_DrawLevelField(xx, yy);
6764 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6765 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6767 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6771 void Impact(int x, int y)
6773 boolean last_line = (y == lev_fieldy - 1);
6774 boolean object_hit = FALSE;
6775 boolean impact = (last_line || object_hit);
6776 int element = Feld[x][y];
6777 int smashed = EL_STEELWALL;
6779 if (!last_line) /* check if element below was hit */
6781 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6784 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6785 MovDir[x][y + 1] != MV_DOWN ||
6786 MovPos[x][y + 1] <= TILEY / 2));
6788 /* do not smash moving elements that left the smashed field in time */
6789 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6790 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6793 #if USE_QUICKSAND_IMPACT_BUGFIX
6794 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6796 RemoveMovingField(x, y + 1);
6797 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6798 Feld[x][y + 2] = EL_ROCK;
6799 TEST_DrawLevelField(x, y + 2);
6804 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6806 RemoveMovingField(x, y + 1);
6807 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6808 Feld[x][y + 2] = EL_ROCK;
6809 TEST_DrawLevelField(x, y + 2);
6816 smashed = MovingOrBlocked2Element(x, y + 1);
6818 impact = (last_line || object_hit);
6821 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6823 SplashAcid(x, y + 1);
6827 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6828 /* only reset graphic animation if graphic really changes after impact */
6830 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6832 ResetGfxAnimation(x, y);
6833 TEST_DrawLevelField(x, y);
6836 if (impact && CAN_EXPLODE_IMPACT(element))
6841 else if (impact && element == EL_PEARL &&
6842 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6844 ResetGfxAnimation(x, y);
6846 Feld[x][y] = EL_PEARL_BREAKING;
6847 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6850 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6852 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6857 if (impact && element == EL_AMOEBA_DROP)
6859 if (object_hit && IS_PLAYER(x, y + 1))
6860 KillPlayerUnlessEnemyProtected(x, y + 1);
6861 else if (object_hit && smashed == EL_PENGUIN)
6865 Feld[x][y] = EL_AMOEBA_GROWING;
6866 Store[x][y] = EL_AMOEBA_WET;
6868 ResetRandomAnimationValue(x, y);
6873 if (object_hit) /* check which object was hit */
6875 if ((CAN_PASS_MAGIC_WALL(element) &&
6876 (smashed == EL_MAGIC_WALL ||
6877 smashed == EL_BD_MAGIC_WALL)) ||
6878 (CAN_PASS_DC_MAGIC_WALL(element) &&
6879 smashed == EL_DC_MAGIC_WALL))
6882 int activated_magic_wall =
6883 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6884 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6885 EL_DC_MAGIC_WALL_ACTIVE);
6887 /* activate magic wall / mill */
6888 SCAN_PLAYFIELD(xx, yy)
6890 if (Feld[xx][yy] == smashed)
6891 Feld[xx][yy] = activated_magic_wall;
6894 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6895 game.magic_wall_active = TRUE;
6897 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6898 SND_MAGIC_WALL_ACTIVATING :
6899 smashed == EL_BD_MAGIC_WALL ?
6900 SND_BD_MAGIC_WALL_ACTIVATING :
6901 SND_DC_MAGIC_WALL_ACTIVATING));
6904 if (IS_PLAYER(x, y + 1))
6906 if (CAN_SMASH_PLAYER(element))
6908 KillPlayerUnlessEnemyProtected(x, y + 1);
6912 else if (smashed == EL_PENGUIN)
6914 if (CAN_SMASH_PLAYER(element))
6920 else if (element == EL_BD_DIAMOND)
6922 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6928 else if (((element == EL_SP_INFOTRON ||
6929 element == EL_SP_ZONK) &&
6930 (smashed == EL_SP_SNIKSNAK ||
6931 smashed == EL_SP_ELECTRON ||
6932 smashed == EL_SP_DISK_ORANGE)) ||
6933 (element == EL_SP_INFOTRON &&
6934 smashed == EL_SP_DISK_YELLOW))
6939 else if (CAN_SMASH_EVERYTHING(element))
6941 if (IS_CLASSIC_ENEMY(smashed) ||
6942 CAN_EXPLODE_SMASHED(smashed))
6947 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6949 if (smashed == EL_LAMP ||
6950 smashed == EL_LAMP_ACTIVE)
6955 else if (smashed == EL_NUT)
6957 Feld[x][y + 1] = EL_NUT_BREAKING;
6958 PlayLevelSound(x, y, SND_NUT_BREAKING);
6959 RaiseScoreElement(EL_NUT);
6962 else if (smashed == EL_PEARL)
6964 ResetGfxAnimation(x, y);
6966 Feld[x][y + 1] = EL_PEARL_BREAKING;
6967 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6970 else if (smashed == EL_DIAMOND)
6972 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6973 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6976 else if (IS_BELT_SWITCH(smashed))
6978 ToggleBeltSwitch(x, y + 1);
6980 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6981 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6982 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6983 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6985 ToggleSwitchgateSwitch(x, y + 1);
6987 else if (smashed == EL_LIGHT_SWITCH ||
6988 smashed == EL_LIGHT_SWITCH_ACTIVE)
6990 ToggleLightSwitch(x, y + 1);
6995 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6998 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7000 CheckElementChangeBySide(x, y + 1, smashed, element,
7001 CE_SWITCHED, CH_SIDE_TOP);
7002 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7008 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7013 /* play sound of magic wall / mill */
7015 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7016 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7017 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7019 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7020 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7021 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7022 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7023 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7024 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7029 /* play sound of object that hits the ground */
7030 if (last_line || object_hit)
7031 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7034 inline static void TurnRoundExt(int x, int y)
7046 { 0, 0 }, { 0, 0 }, { 0, 0 },
7051 int left, right, back;
7055 { MV_DOWN, MV_UP, MV_RIGHT },
7056 { MV_UP, MV_DOWN, MV_LEFT },
7058 { MV_LEFT, MV_RIGHT, MV_DOWN },
7062 { MV_RIGHT, MV_LEFT, MV_UP }
7065 int element = Feld[x][y];
7066 int move_pattern = element_info[element].move_pattern;
7068 int old_move_dir = MovDir[x][y];
7069 int left_dir = turn[old_move_dir].left;
7070 int right_dir = turn[old_move_dir].right;
7071 int back_dir = turn[old_move_dir].back;
7073 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7074 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7075 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7076 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7078 int left_x = x + left_dx, left_y = y + left_dy;
7079 int right_x = x + right_dx, right_y = y + right_dy;
7080 int move_x = x + move_dx, move_y = y + move_dy;
7084 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7086 TestIfBadThingTouchesOtherBadThing(x, y);
7088 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7089 MovDir[x][y] = right_dir;
7090 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7091 MovDir[x][y] = left_dir;
7093 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7095 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7098 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7100 TestIfBadThingTouchesOtherBadThing(x, y);
7102 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7103 MovDir[x][y] = left_dir;
7104 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7105 MovDir[x][y] = right_dir;
7107 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7109 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7112 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7114 TestIfBadThingTouchesOtherBadThing(x, y);
7116 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7117 MovDir[x][y] = left_dir;
7118 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7119 MovDir[x][y] = right_dir;
7121 if (MovDir[x][y] != old_move_dir)
7124 else if (element == EL_YAMYAM)
7126 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7127 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7129 if (can_turn_left && can_turn_right)
7130 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7131 else if (can_turn_left)
7132 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7133 else if (can_turn_right)
7134 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7136 MovDir[x][y] = back_dir;
7138 MovDelay[x][y] = 16 + 16 * RND(3);
7140 else if (element == EL_DARK_YAMYAM)
7142 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7144 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7147 if (can_turn_left && can_turn_right)
7148 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149 else if (can_turn_left)
7150 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151 else if (can_turn_right)
7152 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7154 MovDir[x][y] = back_dir;
7156 MovDelay[x][y] = 16 + 16 * RND(3);
7158 else if (element == EL_PACMAN)
7160 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7161 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7163 if (can_turn_left && can_turn_right)
7164 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7165 else if (can_turn_left)
7166 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7167 else if (can_turn_right)
7168 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170 MovDir[x][y] = back_dir;
7172 MovDelay[x][y] = 6 + RND(40);
7174 else if (element == EL_PIG)
7176 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7177 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7178 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7179 boolean should_turn_left, should_turn_right, should_move_on;
7181 int rnd = RND(rnd_value);
7183 should_turn_left = (can_turn_left &&
7185 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7186 y + back_dy + left_dy)));
7187 should_turn_right = (can_turn_right &&
7189 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7190 y + back_dy + right_dy)));
7191 should_move_on = (can_move_on &&
7194 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7195 y + move_dy + left_dy) ||
7196 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7197 y + move_dy + right_dy)));
7199 if (should_turn_left || should_turn_right || should_move_on)
7201 if (should_turn_left && should_turn_right && should_move_on)
7202 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7203 rnd < 2 * rnd_value / 3 ? right_dir :
7205 else if (should_turn_left && should_turn_right)
7206 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7207 else if (should_turn_left && should_move_on)
7208 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7209 else if (should_turn_right && should_move_on)
7210 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7211 else if (should_turn_left)
7212 MovDir[x][y] = left_dir;
7213 else if (should_turn_right)
7214 MovDir[x][y] = right_dir;
7215 else if (should_move_on)
7216 MovDir[x][y] = old_move_dir;
7218 else if (can_move_on && rnd > rnd_value / 8)
7219 MovDir[x][y] = old_move_dir;
7220 else if (can_turn_left && can_turn_right)
7221 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7222 else if (can_turn_left && rnd > rnd_value / 8)
7223 MovDir[x][y] = left_dir;
7224 else if (can_turn_right && rnd > rnd_value/8)
7225 MovDir[x][y] = right_dir;
7227 MovDir[x][y] = back_dir;
7229 xx = x + move_xy[MovDir[x][y]].dx;
7230 yy = y + move_xy[MovDir[x][y]].dy;
7232 if (!IN_LEV_FIELD(xx, yy) ||
7233 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7234 MovDir[x][y] = old_move_dir;
7238 else if (element == EL_DRAGON)
7240 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7241 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7242 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7244 int rnd = RND(rnd_value);
7246 if (can_move_on && rnd > rnd_value / 8)
7247 MovDir[x][y] = old_move_dir;
7248 else if (can_turn_left && can_turn_right)
7249 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7250 else if (can_turn_left && rnd > rnd_value / 8)
7251 MovDir[x][y] = left_dir;
7252 else if (can_turn_right && rnd > rnd_value / 8)
7253 MovDir[x][y] = right_dir;
7255 MovDir[x][y] = back_dir;
7257 xx = x + move_xy[MovDir[x][y]].dx;
7258 yy = y + move_xy[MovDir[x][y]].dy;
7260 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7261 MovDir[x][y] = old_move_dir;
7265 else if (element == EL_MOLE)
7267 boolean can_move_on =
7268 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7269 IS_AMOEBOID(Feld[move_x][move_y]) ||
7270 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7273 boolean can_turn_left =
7274 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7275 IS_AMOEBOID(Feld[left_x][left_y])));
7277 boolean can_turn_right =
7278 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7279 IS_AMOEBOID(Feld[right_x][right_y])));
7281 if (can_turn_left && can_turn_right)
7282 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7283 else if (can_turn_left)
7284 MovDir[x][y] = left_dir;
7286 MovDir[x][y] = right_dir;
7289 if (MovDir[x][y] != old_move_dir)
7292 else if (element == EL_BALLOON)
7294 MovDir[x][y] = game.wind_direction;
7297 else if (element == EL_SPRING)
7299 #if USE_NEW_SPRING_BUMPER
7300 if (MovDir[x][y] & MV_HORIZONTAL)
7302 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7303 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7305 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7306 ResetGfxAnimation(move_x, move_y);
7307 TEST_DrawLevelField(move_x, move_y);
7309 MovDir[x][y] = back_dir;
7311 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7312 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7313 MovDir[x][y] = MV_NONE;
7316 if (MovDir[x][y] & MV_HORIZONTAL &&
7317 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7318 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7319 MovDir[x][y] = MV_NONE;
7324 else if (element == EL_ROBOT ||
7325 element == EL_SATELLITE ||
7326 element == EL_PENGUIN ||
7327 element == EL_EMC_ANDROID)
7329 int attr_x = -1, attr_y = -1;
7340 for (i = 0; i < MAX_PLAYERS; i++)
7342 struct PlayerInfo *player = &stored_player[i];
7343 int jx = player->jx, jy = player->jy;
7345 if (!player->active)
7349 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7357 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7358 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7359 game.engine_version < VERSION_IDENT(3,1,0,0)))
7365 if (element == EL_PENGUIN)
7368 static int xy[4][2] =
7376 for (i = 0; i < NUM_DIRECTIONS; i++)
7378 int ex = x + xy[i][0];
7379 int ey = y + xy[i][1];
7381 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7382 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7383 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7384 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7393 MovDir[x][y] = MV_NONE;
7395 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7396 else if (attr_x > x)
7397 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7399 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7400 else if (attr_y > y)
7401 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7403 if (element == EL_ROBOT)
7407 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7408 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7409 Moving2Blocked(x, y, &newx, &newy);
7411 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7412 MovDelay[x][y] = 8 + 8 * !RND(3);
7414 MovDelay[x][y] = 16;
7416 else if (element == EL_PENGUIN)
7422 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7424 boolean first_horiz = RND(2);
7425 int new_move_dir = MovDir[x][y];
7428 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7429 Moving2Blocked(x, y, &newx, &newy);
7431 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7435 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7436 Moving2Blocked(x, y, &newx, &newy);
7438 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7441 MovDir[x][y] = old_move_dir;
7445 else if (element == EL_SATELLITE)
7451 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7453 boolean first_horiz = RND(2);
7454 int new_move_dir = MovDir[x][y];
7457 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7458 Moving2Blocked(x, y, &newx, &newy);
7460 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7464 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7465 Moving2Blocked(x, y, &newx, &newy);
7467 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7470 MovDir[x][y] = old_move_dir;
7474 else if (element == EL_EMC_ANDROID)
7476 static int check_pos[16] =
7478 -1, /* 0 => (invalid) */
7479 7, /* 1 => MV_LEFT */
7480 3, /* 2 => MV_RIGHT */
7481 -1, /* 3 => (invalid) */
7483 0, /* 5 => MV_LEFT | MV_UP */
7484 2, /* 6 => MV_RIGHT | MV_UP */
7485 -1, /* 7 => (invalid) */
7486 5, /* 8 => MV_DOWN */
7487 6, /* 9 => MV_LEFT | MV_DOWN */
7488 4, /* 10 => MV_RIGHT | MV_DOWN */
7489 -1, /* 11 => (invalid) */
7490 -1, /* 12 => (invalid) */
7491 -1, /* 13 => (invalid) */
7492 -1, /* 14 => (invalid) */
7493 -1, /* 15 => (invalid) */
7501 { -1, -1, MV_LEFT | MV_UP },
7503 { +1, -1, MV_RIGHT | MV_UP },
7504 { +1, 0, MV_RIGHT },
7505 { +1, +1, MV_RIGHT | MV_DOWN },
7507 { -1, +1, MV_LEFT | MV_DOWN },
7510 int start_pos, check_order;
7511 boolean can_clone = FALSE;
7514 /* check if there is any free field around current position */
7515 for (i = 0; i < 8; i++)
7517 int newx = x + check_xy[i].dx;
7518 int newy = y + check_xy[i].dy;
7520 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7528 if (can_clone) /* randomly find an element to clone */
7532 start_pos = check_pos[RND(8)];
7533 check_order = (RND(2) ? -1 : +1);
7535 for (i = 0; i < 8; i++)
7537 int pos_raw = start_pos + i * check_order;
7538 int pos = (pos_raw + 8) % 8;
7539 int newx = x + check_xy[pos].dx;
7540 int newy = y + check_xy[pos].dy;
7542 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7544 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7545 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7547 Store[x][y] = Feld[newx][newy];
7556 if (can_clone) /* randomly find a direction to move */
7560 start_pos = check_pos[RND(8)];
7561 check_order = (RND(2) ? -1 : +1);
7563 for (i = 0; i < 8; i++)
7565 int pos_raw = start_pos + i * check_order;
7566 int pos = (pos_raw + 8) % 8;
7567 int newx = x + check_xy[pos].dx;
7568 int newy = y + check_xy[pos].dy;
7569 int new_move_dir = check_xy[pos].dir;
7571 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7573 MovDir[x][y] = new_move_dir;
7574 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7583 if (can_clone) /* cloning and moving successful */
7586 /* cannot clone -- try to move towards player */
7588 start_pos = check_pos[MovDir[x][y] & 0x0f];
7589 check_order = (RND(2) ? -1 : +1);
7591 for (i = 0; i < 3; i++)
7593 /* first check start_pos, then previous/next or (next/previous) pos */
7594 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7595 int pos = (pos_raw + 8) % 8;
7596 int newx = x + check_xy[pos].dx;
7597 int newy = y + check_xy[pos].dy;
7598 int new_move_dir = check_xy[pos].dir;
7600 if (IS_PLAYER(newx, newy))
7603 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7605 MovDir[x][y] = new_move_dir;
7606 MovDelay[x][y] = level.android_move_time * 8 + 1;
7613 else if (move_pattern == MV_TURNING_LEFT ||
7614 move_pattern == MV_TURNING_RIGHT ||
7615 move_pattern == MV_TURNING_LEFT_RIGHT ||
7616 move_pattern == MV_TURNING_RIGHT_LEFT ||
7617 move_pattern == MV_TURNING_RANDOM ||
7618 move_pattern == MV_ALL_DIRECTIONS)
7620 boolean can_turn_left =
7621 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7622 boolean can_turn_right =
7623 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7625 if (element_info[element].move_stepsize == 0) /* "not moving" */
7628 if (move_pattern == MV_TURNING_LEFT)
7629 MovDir[x][y] = left_dir;
7630 else if (move_pattern == MV_TURNING_RIGHT)
7631 MovDir[x][y] = right_dir;
7632 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7633 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7634 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7635 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7636 else if (move_pattern == MV_TURNING_RANDOM)
7637 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7638 can_turn_right && !can_turn_left ? right_dir :
7639 RND(2) ? left_dir : right_dir);
7640 else if (can_turn_left && can_turn_right)
7641 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7642 else if (can_turn_left)
7643 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7644 else if (can_turn_right)
7645 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7647 MovDir[x][y] = back_dir;
7649 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7651 else if (move_pattern == MV_HORIZONTAL ||
7652 move_pattern == MV_VERTICAL)
7654 if (move_pattern & old_move_dir)
7655 MovDir[x][y] = back_dir;
7656 else if (move_pattern == MV_HORIZONTAL)
7657 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7658 else if (move_pattern == MV_VERTICAL)
7659 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7661 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7663 else if (move_pattern & MV_ANY_DIRECTION)
7665 MovDir[x][y] = move_pattern;
7666 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7668 else if (move_pattern & MV_WIND_DIRECTION)
7670 MovDir[x][y] = game.wind_direction;
7671 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7673 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7675 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7676 MovDir[x][y] = left_dir;
7677 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7678 MovDir[x][y] = right_dir;
7680 if (MovDir[x][y] != old_move_dir)
7681 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7683 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7685 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7686 MovDir[x][y] = right_dir;
7687 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7688 MovDir[x][y] = left_dir;
7690 if (MovDir[x][y] != old_move_dir)
7691 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7693 else if (move_pattern == MV_TOWARDS_PLAYER ||
7694 move_pattern == MV_AWAY_FROM_PLAYER)
7696 int attr_x = -1, attr_y = -1;
7698 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7709 for (i = 0; i < MAX_PLAYERS; i++)
7711 struct PlayerInfo *player = &stored_player[i];
7712 int jx = player->jx, jy = player->jy;
7714 if (!player->active)
7718 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7726 MovDir[x][y] = MV_NONE;
7728 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7729 else if (attr_x > x)
7730 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7732 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7733 else if (attr_y > y)
7734 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7736 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7740 boolean first_horiz = RND(2);
7741 int new_move_dir = MovDir[x][y];
7743 if (element_info[element].move_stepsize == 0) /* "not moving" */
7745 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7746 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7752 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7753 Moving2Blocked(x, y, &newx, &newy);
7755 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7759 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7760 Moving2Blocked(x, y, &newx, &newy);
7762 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7765 MovDir[x][y] = old_move_dir;
7768 else if (move_pattern == MV_WHEN_PUSHED ||
7769 move_pattern == MV_WHEN_DROPPED)
7771 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7772 MovDir[x][y] = MV_NONE;
7776 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7778 static int test_xy[7][2] =
7788 static int test_dir[7] =
7798 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7799 int move_preference = -1000000; /* start with very low preference */
7800 int new_move_dir = MV_NONE;
7801 int start_test = RND(4);
7804 for (i = 0; i < NUM_DIRECTIONS; i++)
7806 int move_dir = test_dir[start_test + i];
7807 int move_dir_preference;
7809 xx = x + test_xy[start_test + i][0];
7810 yy = y + test_xy[start_test + i][1];
7812 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7813 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7815 new_move_dir = move_dir;
7820 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7823 move_dir_preference = -1 * RunnerVisit[xx][yy];
7824 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7825 move_dir_preference = PlayerVisit[xx][yy];
7827 if (move_dir_preference > move_preference)
7829 /* prefer field that has not been visited for the longest time */
7830 move_preference = move_dir_preference;
7831 new_move_dir = move_dir;
7833 else if (move_dir_preference == move_preference &&
7834 move_dir == old_move_dir)
7836 /* prefer last direction when all directions are preferred equally */
7837 move_preference = move_dir_preference;
7838 new_move_dir = move_dir;
7842 MovDir[x][y] = new_move_dir;
7843 if (old_move_dir != new_move_dir)
7844 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7848 static void TurnRound(int x, int y)
7850 int direction = MovDir[x][y];
7854 GfxDir[x][y] = MovDir[x][y];
7856 if (direction != MovDir[x][y])
7860 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7862 ResetGfxFrame(x, y, FALSE);
7865 static boolean JustBeingPushed(int x, int y)
7869 for (i = 0; i < MAX_PLAYERS; i++)
7871 struct PlayerInfo *player = &stored_player[i];
7873 if (player->active && player->is_pushing && player->MovPos)
7875 int next_jx = player->jx + (player->jx - player->last_jx);
7876 int next_jy = player->jy + (player->jy - player->last_jy);
7878 if (x == next_jx && y == next_jy)
7886 void StartMoving(int x, int y)
7888 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7889 int element = Feld[x][y];
7894 if (MovDelay[x][y] == 0)
7895 GfxAction[x][y] = ACTION_DEFAULT;
7897 if (CAN_FALL(element) && y < lev_fieldy - 1)
7899 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7900 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7901 if (JustBeingPushed(x, y))
7904 if (element == EL_QUICKSAND_FULL)
7906 if (IS_FREE(x, y + 1))
7908 InitMovingField(x, y, MV_DOWN);
7909 started_moving = TRUE;
7911 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7912 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7913 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7914 Store[x][y] = EL_ROCK;
7916 Store[x][y] = EL_ROCK;
7919 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7921 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7923 if (!MovDelay[x][y])
7925 MovDelay[x][y] = TILEY + 1;
7927 ResetGfxAnimation(x, y);
7928 ResetGfxAnimation(x, y + 1);
7933 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7934 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7941 Feld[x][y] = EL_QUICKSAND_EMPTY;
7942 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7943 Store[x][y + 1] = Store[x][y];
7946 PlayLevelSoundAction(x, y, ACTION_FILLING);
7948 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7950 if (!MovDelay[x][y])
7952 MovDelay[x][y] = TILEY + 1;
7954 ResetGfxAnimation(x, y);
7955 ResetGfxAnimation(x, y + 1);
7960 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7961 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7968 Feld[x][y] = EL_QUICKSAND_EMPTY;
7969 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7970 Store[x][y + 1] = Store[x][y];
7973 PlayLevelSoundAction(x, y, ACTION_FILLING);
7976 else if (element == EL_QUICKSAND_FAST_FULL)
7978 if (IS_FREE(x, y + 1))
7980 InitMovingField(x, y, MV_DOWN);
7981 started_moving = TRUE;
7983 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7984 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7985 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7986 Store[x][y] = EL_ROCK;
7988 Store[x][y] = EL_ROCK;
7991 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7993 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7995 if (!MovDelay[x][y])
7997 MovDelay[x][y] = TILEY + 1;
7999 ResetGfxAnimation(x, y);
8000 ResetGfxAnimation(x, y + 1);
8005 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8006 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8013 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8014 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8015 Store[x][y + 1] = Store[x][y];
8018 PlayLevelSoundAction(x, y, ACTION_FILLING);
8020 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8022 if (!MovDelay[x][y])
8024 MovDelay[x][y] = TILEY + 1;
8026 ResetGfxAnimation(x, y);
8027 ResetGfxAnimation(x, y + 1);
8032 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8033 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8040 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8041 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8042 Store[x][y + 1] = Store[x][y];
8045 PlayLevelSoundAction(x, y, ACTION_FILLING);
8048 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8049 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8051 InitMovingField(x, y, MV_DOWN);
8052 started_moving = TRUE;
8054 Feld[x][y] = EL_QUICKSAND_FILLING;
8055 Store[x][y] = element;
8057 PlayLevelSoundAction(x, y, ACTION_FILLING);
8059 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8060 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8062 InitMovingField(x, y, MV_DOWN);
8063 started_moving = TRUE;
8065 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8066 Store[x][y] = element;
8068 PlayLevelSoundAction(x, y, ACTION_FILLING);
8070 else if (element == EL_MAGIC_WALL_FULL)
8072 if (IS_FREE(x, y + 1))
8074 InitMovingField(x, y, MV_DOWN);
8075 started_moving = TRUE;
8077 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8078 Store[x][y] = EL_CHANGED(Store[x][y]);
8080 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8082 if (!MovDelay[x][y])
8083 MovDelay[x][y] = TILEY/4 + 1;
8092 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8093 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8094 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8098 else if (element == EL_BD_MAGIC_WALL_FULL)
8100 if (IS_FREE(x, y + 1))
8102 InitMovingField(x, y, MV_DOWN);
8103 started_moving = TRUE;
8105 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8106 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8108 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8110 if (!MovDelay[x][y])
8111 MovDelay[x][y] = TILEY/4 + 1;
8120 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8121 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8122 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8126 else if (element == EL_DC_MAGIC_WALL_FULL)
8128 if (IS_FREE(x, y + 1))
8130 InitMovingField(x, y, MV_DOWN);
8131 started_moving = TRUE;
8133 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8134 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8136 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8138 if (!MovDelay[x][y])
8139 MovDelay[x][y] = TILEY/4 + 1;
8148 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8149 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8150 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8154 else if ((CAN_PASS_MAGIC_WALL(element) &&
8155 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8156 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8157 (CAN_PASS_DC_MAGIC_WALL(element) &&
8158 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8161 InitMovingField(x, y, MV_DOWN);
8162 started_moving = TRUE;
8165 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8166 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8167 EL_DC_MAGIC_WALL_FILLING);
8168 Store[x][y] = element;
8170 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8172 SplashAcid(x, y + 1);
8174 InitMovingField(x, y, MV_DOWN);
8175 started_moving = TRUE;
8177 Store[x][y] = EL_ACID;
8180 #if USE_FIX_IMPACT_COLLISION
8181 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8182 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8184 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8185 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8187 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8188 CAN_FALL(element) && WasJustFalling[x][y] &&
8189 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8191 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8192 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8193 (Feld[x][y + 1] == EL_BLOCKED)))
8195 /* this is needed for a special case not covered by calling "Impact()"
8196 from "ContinueMoving()": if an element moves to a tile directly below
8197 another element which was just falling on that tile (which was empty
8198 in the previous frame), the falling element above would just stop
8199 instead of smashing the element below (in previous version, the above
8200 element was just checked for "moving" instead of "falling", resulting
8201 in incorrect smashes caused by horizontal movement of the above
8202 element; also, the case of the player being the element to smash was
8203 simply not covered here... :-/ ) */
8205 CheckCollision[x][y] = 0;
8206 CheckImpact[x][y] = 0;
8210 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8212 if (MovDir[x][y] == MV_NONE)
8214 InitMovingField(x, y, MV_DOWN);
8215 started_moving = TRUE;
8218 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8220 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8221 MovDir[x][y] = MV_DOWN;
8223 InitMovingField(x, y, MV_DOWN);
8224 started_moving = TRUE;
8226 else if (element == EL_AMOEBA_DROP)
8228 Feld[x][y] = EL_AMOEBA_GROWING;
8229 Store[x][y] = EL_AMOEBA_WET;
8231 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8232 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8233 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8234 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8236 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8237 (IS_FREE(x - 1, y + 1) ||
8238 Feld[x - 1][y + 1] == EL_ACID));
8239 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8240 (IS_FREE(x + 1, y + 1) ||
8241 Feld[x + 1][y + 1] == EL_ACID));
8242 boolean can_fall_any = (can_fall_left || can_fall_right);
8243 boolean can_fall_both = (can_fall_left && can_fall_right);
8244 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8246 #if USE_NEW_ALL_SLIPPERY
8247 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8249 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8250 can_fall_right = FALSE;
8251 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8252 can_fall_left = FALSE;
8253 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8254 can_fall_right = FALSE;
8255 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8256 can_fall_left = FALSE;
8258 can_fall_any = (can_fall_left || can_fall_right);
8259 can_fall_both = FALSE;
8262 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8264 if (slippery_type == SLIPPERY_ONLY_LEFT)
8265 can_fall_right = FALSE;
8266 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8267 can_fall_left = FALSE;
8268 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8269 can_fall_right = FALSE;
8270 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8271 can_fall_left = FALSE;
8273 can_fall_any = (can_fall_left || can_fall_right);
8274 can_fall_both = (can_fall_left && can_fall_right);
8278 #if USE_NEW_ALL_SLIPPERY
8280 #if USE_NEW_SP_SLIPPERY
8281 /* !!! better use the same properties as for custom elements here !!! */
8282 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8283 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8285 can_fall_right = FALSE; /* slip down on left side */
8286 can_fall_both = FALSE;
8291 #if USE_NEW_ALL_SLIPPERY
8294 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8295 can_fall_right = FALSE; /* slip down on left side */
8297 can_fall_left = !(can_fall_right = RND(2));
8299 can_fall_both = FALSE;
8304 if (game.emulation == EMU_BOULDERDASH ||
8305 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8306 can_fall_right = FALSE; /* slip down on left side */
8308 can_fall_left = !(can_fall_right = RND(2));
8310 can_fall_both = FALSE;
8316 /* if not determined otherwise, prefer left side for slipping down */
8317 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8318 started_moving = TRUE;
8322 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8324 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8327 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8328 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8329 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8330 int belt_dir = game.belt_dir[belt_nr];
8332 if ((belt_dir == MV_LEFT && left_is_free) ||
8333 (belt_dir == MV_RIGHT && right_is_free))
8335 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8337 InitMovingField(x, y, belt_dir);
8338 started_moving = TRUE;
8340 Pushed[x][y] = TRUE;
8341 Pushed[nextx][y] = TRUE;
8343 GfxAction[x][y] = ACTION_DEFAULT;
8347 MovDir[x][y] = 0; /* if element was moving, stop it */
8352 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8354 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8356 if (CAN_MOVE(element) && !started_moving)
8359 int move_pattern = element_info[element].move_pattern;
8364 if (MovDir[x][y] == MV_NONE)
8366 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8367 x, y, element, element_info[element].token_name);
8368 printf("StartMoving(): This should never happen!\n");
8373 Moving2Blocked(x, y, &newx, &newy);
8375 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8378 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8379 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8381 WasJustMoving[x][y] = 0;
8382 CheckCollision[x][y] = 0;
8384 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8386 if (Feld[x][y] != element) /* element has changed */
8390 if (!MovDelay[x][y]) /* start new movement phase */
8392 /* all objects that can change their move direction after each step
8393 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8395 if (element != EL_YAMYAM &&
8396 element != EL_DARK_YAMYAM &&
8397 element != EL_PACMAN &&
8398 !(move_pattern & MV_ANY_DIRECTION) &&
8399 move_pattern != MV_TURNING_LEFT &&
8400 move_pattern != MV_TURNING_RIGHT &&
8401 move_pattern != MV_TURNING_LEFT_RIGHT &&
8402 move_pattern != MV_TURNING_RIGHT_LEFT &&
8403 move_pattern != MV_TURNING_RANDOM)
8407 if (MovDelay[x][y] && (element == EL_BUG ||
8408 element == EL_SPACESHIP ||
8409 element == EL_SP_SNIKSNAK ||
8410 element == EL_SP_ELECTRON ||
8411 element == EL_MOLE))
8412 TEST_DrawLevelField(x, y);
8416 if (MovDelay[x][y]) /* wait some time before next movement */
8420 if (element == EL_ROBOT ||
8421 element == EL_YAMYAM ||
8422 element == EL_DARK_YAMYAM)
8424 DrawLevelElementAnimationIfNeeded(x, y, element);
8425 PlayLevelSoundAction(x, y, ACTION_WAITING);
8427 else if (element == EL_SP_ELECTRON)
8428 DrawLevelElementAnimationIfNeeded(x, y, element);
8429 else if (element == EL_DRAGON)
8432 int dir = MovDir[x][y];
8433 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8434 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8435 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8436 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8437 dir == MV_UP ? IMG_FLAMES_1_UP :
8438 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8439 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8441 GfxAction[x][y] = ACTION_ATTACKING;
8443 if (IS_PLAYER(x, y))
8444 DrawPlayerField(x, y);
8446 TEST_DrawLevelField(x, y);
8448 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8450 for (i = 1; i <= 3; i++)
8452 int xx = x + i * dx;
8453 int yy = y + i * dy;
8454 int sx = SCREENX(xx);
8455 int sy = SCREENY(yy);
8456 int flame_graphic = graphic + (i - 1);
8458 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8463 int flamed = MovingOrBlocked2Element(xx, yy);
8467 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8469 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8470 RemoveMovingField(xx, yy);
8472 RemoveField(xx, yy);
8474 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8477 RemoveMovingField(xx, yy);
8480 ChangeDelay[xx][yy] = 0;
8482 Feld[xx][yy] = EL_FLAMES;
8484 if (IN_SCR_FIELD(sx, sy))
8486 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8487 DrawGraphic(sx, sy, flame_graphic, frame);
8492 if (Feld[xx][yy] == EL_FLAMES)
8493 Feld[xx][yy] = EL_EMPTY;
8494 TEST_DrawLevelField(xx, yy);
8499 if (MovDelay[x][y]) /* element still has to wait some time */
8501 PlayLevelSoundAction(x, y, ACTION_WAITING);
8507 /* now make next step */
8509 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8511 if (DONT_COLLIDE_WITH(element) &&
8512 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8513 !PLAYER_ENEMY_PROTECTED(newx, newy))
8515 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8520 else if (CAN_MOVE_INTO_ACID(element) &&
8521 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8522 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8523 (MovDir[x][y] == MV_DOWN ||
8524 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8526 SplashAcid(newx, newy);
8527 Store[x][y] = EL_ACID;
8529 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8531 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8532 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8533 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8534 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8537 TEST_DrawLevelField(x, y);
8539 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8540 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8541 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8543 local_player->friends_still_needed--;
8544 if (!local_player->friends_still_needed &&
8545 !local_player->GameOver && AllPlayersGone)
8546 PlayerWins(local_player);
8550 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8552 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8553 TEST_DrawLevelField(newx, newy);
8555 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8557 else if (!IS_FREE(newx, newy))
8559 GfxAction[x][y] = ACTION_WAITING;
8561 if (IS_PLAYER(x, y))
8562 DrawPlayerField(x, y);
8564 TEST_DrawLevelField(x, y);
8569 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8571 if (IS_FOOD_PIG(Feld[newx][newy]))
8573 if (IS_MOVING(newx, newy))
8574 RemoveMovingField(newx, newy);
8577 Feld[newx][newy] = EL_EMPTY;
8578 TEST_DrawLevelField(newx, newy);
8581 PlayLevelSound(x, y, SND_PIG_DIGGING);
8583 else if (!IS_FREE(newx, newy))
8585 if (IS_PLAYER(x, y))
8586 DrawPlayerField(x, y);
8588 TEST_DrawLevelField(x, y);
8593 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8595 if (Store[x][y] != EL_EMPTY)
8597 boolean can_clone = FALSE;
8600 /* check if element to clone is still there */
8601 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8603 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8611 /* cannot clone or target field not free anymore -- do not clone */
8612 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8613 Store[x][y] = EL_EMPTY;
8616 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8618 if (IS_MV_DIAGONAL(MovDir[x][y]))
8620 int diagonal_move_dir = MovDir[x][y];
8621 int stored = Store[x][y];
8622 int change_delay = 8;
8625 /* android is moving diagonally */
8627 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8629 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8630 GfxElement[x][y] = EL_EMC_ANDROID;
8631 GfxAction[x][y] = ACTION_SHRINKING;
8632 GfxDir[x][y] = diagonal_move_dir;
8633 ChangeDelay[x][y] = change_delay;
8635 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8638 DrawLevelGraphicAnimation(x, y, graphic);
8639 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8641 if (Feld[newx][newy] == EL_ACID)
8643 SplashAcid(newx, newy);
8648 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8650 Store[newx][newy] = EL_EMC_ANDROID;
8651 GfxElement[newx][newy] = EL_EMC_ANDROID;
8652 GfxAction[newx][newy] = ACTION_GROWING;
8653 GfxDir[newx][newy] = diagonal_move_dir;
8654 ChangeDelay[newx][newy] = change_delay;
8656 graphic = el_act_dir2img(GfxElement[newx][newy],
8657 GfxAction[newx][newy], GfxDir[newx][newy]);
8659 DrawLevelGraphicAnimation(newx, newy, graphic);
8660 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8666 Feld[newx][newy] = EL_EMPTY;
8667 TEST_DrawLevelField(newx, newy);
8669 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8672 else if (!IS_FREE(newx, newy))
8675 if (IS_PLAYER(x, y))
8676 DrawPlayerField(x, y);
8678 TEST_DrawLevelField(x, y);
8684 else if (IS_CUSTOM_ELEMENT(element) &&
8685 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8688 if (!DigFieldByCE(newx, newy, element))
8691 int new_element = Feld[newx][newy];
8693 if (!IS_FREE(newx, newy))
8695 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8696 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8699 /* no element can dig solid indestructible elements */
8700 if (IS_INDESTRUCTIBLE(new_element) &&
8701 !IS_DIGGABLE(new_element) &&
8702 !IS_COLLECTIBLE(new_element))
8705 if (AmoebaNr[newx][newy] &&
8706 (new_element == EL_AMOEBA_FULL ||
8707 new_element == EL_BD_AMOEBA ||
8708 new_element == EL_AMOEBA_GROWING))
8710 AmoebaCnt[AmoebaNr[newx][newy]]--;
8711 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8714 if (IS_MOVING(newx, newy))
8715 RemoveMovingField(newx, newy);
8718 RemoveField(newx, newy);
8719 TEST_DrawLevelField(newx, newy);
8722 /* if digged element was about to explode, prevent the explosion */
8723 ExplodeField[newx][newy] = EX_TYPE_NONE;
8725 PlayLevelSoundAction(x, y, action);
8728 Store[newx][newy] = EL_EMPTY;
8731 /* this makes it possible to leave the removed element again */
8732 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8733 Store[newx][newy] = new_element;
8735 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8737 int move_leave_element = element_info[element].move_leave_element;
8739 /* this makes it possible to leave the removed element again */
8740 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8741 new_element : move_leave_element);
8747 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8749 RunnerVisit[x][y] = FrameCounter;
8750 PlayerVisit[x][y] /= 8; /* expire player visit path */
8753 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8755 if (!IS_FREE(newx, newy))
8757 if (IS_PLAYER(x, y))
8758 DrawPlayerField(x, y);
8760 TEST_DrawLevelField(x, y);
8766 boolean wanna_flame = !RND(10);
8767 int dx = newx - x, dy = newy - y;
8768 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8769 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8770 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8771 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8772 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8773 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8776 IS_CLASSIC_ENEMY(element1) ||
8777 IS_CLASSIC_ENEMY(element2)) &&
8778 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8779 element1 != EL_FLAMES && element2 != EL_FLAMES)
8781 ResetGfxAnimation(x, y);
8782 GfxAction[x][y] = ACTION_ATTACKING;
8784 if (IS_PLAYER(x, y))
8785 DrawPlayerField(x, y);
8787 TEST_DrawLevelField(x, y);
8789 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8791 MovDelay[x][y] = 50;
8795 RemoveField(newx, newy);
8797 Feld[newx][newy] = EL_FLAMES;
8798 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8801 RemoveField(newx1, newy1);
8803 Feld[newx1][newy1] = EL_FLAMES;
8805 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8808 RemoveField(newx2, newy2);
8810 Feld[newx2][newy2] = EL_FLAMES;
8817 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8818 Feld[newx][newy] == EL_DIAMOND)
8820 if (IS_MOVING(newx, newy))
8821 RemoveMovingField(newx, newy);
8824 Feld[newx][newy] = EL_EMPTY;
8825 TEST_DrawLevelField(newx, newy);
8828 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8830 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8831 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8833 if (AmoebaNr[newx][newy])
8835 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8836 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8837 Feld[newx][newy] == EL_BD_AMOEBA)
8838 AmoebaCnt[AmoebaNr[newx][newy]]--;
8843 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8845 RemoveMovingField(newx, newy);
8848 if (IS_MOVING(newx, newy))
8850 RemoveMovingField(newx, newy);
8855 Feld[newx][newy] = EL_EMPTY;
8856 TEST_DrawLevelField(newx, newy);
8859 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8861 else if ((element == EL_PACMAN || element == EL_MOLE)
8862 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8864 if (AmoebaNr[newx][newy])
8866 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8867 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8868 Feld[newx][newy] == EL_BD_AMOEBA)
8869 AmoebaCnt[AmoebaNr[newx][newy]]--;
8872 if (element == EL_MOLE)
8874 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8875 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8877 ResetGfxAnimation(x, y);
8878 GfxAction[x][y] = ACTION_DIGGING;
8879 TEST_DrawLevelField(x, y);
8881 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8883 return; /* wait for shrinking amoeba */
8885 else /* element == EL_PACMAN */
8887 Feld[newx][newy] = EL_EMPTY;
8888 TEST_DrawLevelField(newx, newy);
8889 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8892 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8893 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8894 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8896 /* wait for shrinking amoeba to completely disappear */
8899 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8901 /* object was running against a wall */
8906 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8907 if (move_pattern & MV_ANY_DIRECTION &&
8908 move_pattern == MovDir[x][y])
8910 int blocking_element =
8911 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8913 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8916 element = Feld[x][y]; /* element might have changed */
8920 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8921 DrawLevelElementAnimation(x, y, element);
8923 if (DONT_TOUCH(element))
8924 TestIfBadThingTouchesPlayer(x, y);
8929 InitMovingField(x, y, MovDir[x][y]);
8931 PlayLevelSoundAction(x, y, ACTION_MOVING);
8935 ContinueMoving(x, y);
8938 void ContinueMoving(int x, int y)
8940 int element = Feld[x][y];
8941 struct ElementInfo *ei = &element_info[element];
8942 int direction = MovDir[x][y];
8943 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8944 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8945 int newx = x + dx, newy = y + dy;
8946 int stored = Store[x][y];
8947 int stored_new = Store[newx][newy];
8948 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8949 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8950 boolean last_line = (newy == lev_fieldy - 1);
8952 MovPos[x][y] += getElementMoveStepsize(x, y);
8954 if (pushed_by_player) /* special case: moving object pushed by player */
8955 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8957 if (ABS(MovPos[x][y]) < TILEX)
8960 int ee = Feld[x][y];
8961 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8962 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8964 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8965 x, y, ABS(MovPos[x][y]),
8967 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8970 TEST_DrawLevelField(x, y);
8972 return; /* element is still moving */
8975 /* element reached destination field */
8977 Feld[x][y] = EL_EMPTY;
8978 Feld[newx][newy] = element;
8979 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8981 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8983 element = Feld[newx][newy] = EL_ACID;
8985 else if (element == EL_MOLE)
8987 Feld[x][y] = EL_SAND;
8989 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8991 else if (element == EL_QUICKSAND_FILLING)
8993 element = Feld[newx][newy] = get_next_element(element);
8994 Store[newx][newy] = Store[x][y];
8996 else if (element == EL_QUICKSAND_EMPTYING)
8998 Feld[x][y] = get_next_element(element);
8999 element = Feld[newx][newy] = Store[x][y];
9001 else if (element == EL_QUICKSAND_FAST_FILLING)
9003 element = Feld[newx][newy] = get_next_element(element);
9004 Store[newx][newy] = Store[x][y];
9006 else if (element == EL_QUICKSAND_FAST_EMPTYING)
9008 Feld[x][y] = get_next_element(element);
9009 element = Feld[newx][newy] = Store[x][y];
9011 else if (element == EL_MAGIC_WALL_FILLING)
9013 element = Feld[newx][newy] = get_next_element(element);
9014 if (!game.magic_wall_active)
9015 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9016 Store[newx][newy] = Store[x][y];
9018 else if (element == EL_MAGIC_WALL_EMPTYING)
9020 Feld[x][y] = get_next_element(element);
9021 if (!game.magic_wall_active)
9022 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9023 element = Feld[newx][newy] = Store[x][y];
9025 #if USE_NEW_CUSTOM_VALUE
9026 InitField(newx, newy, FALSE);
9029 else if (element == EL_BD_MAGIC_WALL_FILLING)
9031 element = Feld[newx][newy] = get_next_element(element);
9032 if (!game.magic_wall_active)
9033 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9034 Store[newx][newy] = Store[x][y];
9036 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9038 Feld[x][y] = get_next_element(element);
9039 if (!game.magic_wall_active)
9040 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9041 element = Feld[newx][newy] = Store[x][y];
9043 #if USE_NEW_CUSTOM_VALUE
9044 InitField(newx, newy, FALSE);
9047 else if (element == EL_DC_MAGIC_WALL_FILLING)
9049 element = Feld[newx][newy] = get_next_element(element);
9050 if (!game.magic_wall_active)
9051 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9052 Store[newx][newy] = Store[x][y];
9054 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9056 Feld[x][y] = get_next_element(element);
9057 if (!game.magic_wall_active)
9058 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9059 element = Feld[newx][newy] = Store[x][y];
9061 #if USE_NEW_CUSTOM_VALUE
9062 InitField(newx, newy, FALSE);
9065 else if (element == EL_AMOEBA_DROPPING)
9067 Feld[x][y] = get_next_element(element);
9068 element = Feld[newx][newy] = Store[x][y];
9070 else if (element == EL_SOKOBAN_OBJECT)
9073 Feld[x][y] = Back[x][y];
9075 if (Back[newx][newy])
9076 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9078 Back[x][y] = Back[newx][newy] = 0;
9081 Store[x][y] = EL_EMPTY;
9086 MovDelay[newx][newy] = 0;
9088 if (CAN_CHANGE_OR_HAS_ACTION(element))
9090 /* copy element change control values to new field */
9091 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9092 ChangePage[newx][newy] = ChangePage[x][y];
9093 ChangeCount[newx][newy] = ChangeCount[x][y];
9094 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9097 #if USE_NEW_CUSTOM_VALUE
9098 CustomValue[newx][newy] = CustomValue[x][y];
9101 ChangeDelay[x][y] = 0;
9102 ChangePage[x][y] = -1;
9103 ChangeCount[x][y] = 0;
9104 ChangeEvent[x][y] = -1;
9106 #if USE_NEW_CUSTOM_VALUE
9107 CustomValue[x][y] = 0;
9110 /* copy animation control values to new field */
9111 GfxFrame[newx][newy] = GfxFrame[x][y];
9112 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9113 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9114 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9116 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9118 /* some elements can leave other elements behind after moving */
9120 if (ei->move_leave_element != EL_EMPTY &&
9121 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9122 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9124 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9125 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9126 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9129 int move_leave_element = ei->move_leave_element;
9133 /* this makes it possible to leave the removed element again */
9134 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9135 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9137 /* this makes it possible to leave the removed element again */
9138 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9139 move_leave_element = stored;
9142 /* this makes it possible to leave the removed element again */
9143 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9144 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9145 move_leave_element = stored;
9148 Feld[x][y] = move_leave_element;
9150 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9151 MovDir[x][y] = direction;
9153 InitField(x, y, FALSE);
9155 if (GFX_CRUMBLED(Feld[x][y]))
9156 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9158 if (ELEM_IS_PLAYER(move_leave_element))
9159 RelocatePlayer(x, y, move_leave_element);
9162 /* do this after checking for left-behind element */
9163 ResetGfxAnimation(x, y); /* reset animation values for old field */
9165 if (!CAN_MOVE(element) ||
9166 (CAN_FALL(element) && direction == MV_DOWN &&
9167 (element == EL_SPRING ||
9168 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9169 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9170 GfxDir[x][y] = MovDir[newx][newy] = 0;
9172 TEST_DrawLevelField(x, y);
9173 TEST_DrawLevelField(newx, newy);
9175 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9177 /* prevent pushed element from moving on in pushed direction */
9178 if (pushed_by_player && CAN_MOVE(element) &&
9179 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9180 !(element_info[element].move_pattern & direction))
9181 TurnRound(newx, newy);
9183 /* prevent elements on conveyor belt from moving on in last direction */
9184 if (pushed_by_conveyor && CAN_FALL(element) &&
9185 direction & MV_HORIZONTAL)
9186 MovDir[newx][newy] = 0;
9188 if (!pushed_by_player)
9190 int nextx = newx + dx, nexty = newy + dy;
9191 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9193 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9195 if (CAN_FALL(element) && direction == MV_DOWN)
9196 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9198 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9199 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9201 #if USE_FIX_IMPACT_COLLISION
9202 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9203 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9207 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9209 TestIfBadThingTouchesPlayer(newx, newy);
9210 TestIfBadThingTouchesFriend(newx, newy);
9212 if (!IS_CUSTOM_ELEMENT(element))
9213 TestIfBadThingTouchesOtherBadThing(newx, newy);
9215 else if (element == EL_PENGUIN)
9216 TestIfFriendTouchesBadThing(newx, newy);
9218 if (DONT_GET_HIT_BY(element))
9220 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9223 /* give the player one last chance (one more frame) to move away */
9224 if (CAN_FALL(element) && direction == MV_DOWN &&
9225 (last_line || (!IS_FREE(x, newy + 1) &&
9226 (!IS_PLAYER(x, newy + 1) ||
9227 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9230 if (pushed_by_player && !game.use_change_when_pushing_bug)
9232 int push_side = MV_DIR_OPPOSITE(direction);
9233 struct PlayerInfo *player = PLAYERINFO(x, y);
9235 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9236 player->index_bit, push_side);
9237 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9238 player->index_bit, push_side);
9241 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9242 MovDelay[newx][newy] = 1;
9244 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9246 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9249 if (ChangePage[newx][newy] != -1) /* delayed change */
9251 int page = ChangePage[newx][newy];
9252 struct ElementChangeInfo *change = &ei->change_page[page];
9254 ChangePage[newx][newy] = -1;
9256 if (change->can_change)
9258 if (ChangeElement(newx, newy, element, page))
9260 if (change->post_change_function)
9261 change->post_change_function(newx, newy);
9265 if (change->has_action)
9266 ExecuteCustomElementAction(newx, newy, element, page);
9270 TestIfElementHitsCustomElement(newx, newy, direction);
9271 TestIfPlayerTouchesCustomElement(newx, newy);
9272 TestIfElementTouchesCustomElement(newx, newy);
9274 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9275 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9276 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9277 MV_DIR_OPPOSITE(direction));
9280 int AmoebeNachbarNr(int ax, int ay)
9283 int element = Feld[ax][ay];
9285 static int xy[4][2] =
9293 for (i = 0; i < NUM_DIRECTIONS; i++)
9295 int x = ax + xy[i][0];
9296 int y = ay + xy[i][1];
9298 if (!IN_LEV_FIELD(x, y))
9301 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9302 group_nr = AmoebaNr[x][y];
9308 void AmoebenVereinigen(int ax, int ay)
9310 int i, x, y, xx, yy;
9311 int new_group_nr = AmoebaNr[ax][ay];
9312 static int xy[4][2] =
9320 if (new_group_nr == 0)
9323 for (i = 0; i < NUM_DIRECTIONS; i++)
9328 if (!IN_LEV_FIELD(x, y))
9331 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9332 Feld[x][y] == EL_BD_AMOEBA ||
9333 Feld[x][y] == EL_AMOEBA_DEAD) &&
9334 AmoebaNr[x][y] != new_group_nr)
9336 int old_group_nr = AmoebaNr[x][y];
9338 if (old_group_nr == 0)
9341 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9342 AmoebaCnt[old_group_nr] = 0;
9343 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9344 AmoebaCnt2[old_group_nr] = 0;
9346 SCAN_PLAYFIELD(xx, yy)
9348 if (AmoebaNr[xx][yy] == old_group_nr)
9349 AmoebaNr[xx][yy] = new_group_nr;
9355 void AmoebeUmwandeln(int ax, int ay)
9359 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9361 int group_nr = AmoebaNr[ax][ay];
9366 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9367 printf("AmoebeUmwandeln(): This should never happen!\n");
9372 SCAN_PLAYFIELD(x, y)
9374 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9377 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9381 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9382 SND_AMOEBA_TURNING_TO_GEM :
9383 SND_AMOEBA_TURNING_TO_ROCK));
9388 static int xy[4][2] =
9396 for (i = 0; i < NUM_DIRECTIONS; i++)
9401 if (!IN_LEV_FIELD(x, y))
9404 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9406 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9407 SND_AMOEBA_TURNING_TO_GEM :
9408 SND_AMOEBA_TURNING_TO_ROCK));
9415 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9418 int group_nr = AmoebaNr[ax][ay];
9419 boolean done = FALSE;
9424 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9425 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9430 SCAN_PLAYFIELD(x, y)
9432 if (AmoebaNr[x][y] == group_nr &&
9433 (Feld[x][y] == EL_AMOEBA_DEAD ||
9434 Feld[x][y] == EL_BD_AMOEBA ||
9435 Feld[x][y] == EL_AMOEBA_GROWING))
9438 Feld[x][y] = new_element;
9439 InitField(x, y, FALSE);
9440 TEST_DrawLevelField(x, y);
9446 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9447 SND_BD_AMOEBA_TURNING_TO_ROCK :
9448 SND_BD_AMOEBA_TURNING_TO_GEM));
9451 void AmoebeWaechst(int x, int y)
9453 static unsigned long sound_delay = 0;
9454 static unsigned long sound_delay_value = 0;
9456 if (!MovDelay[x][y]) /* start new growing cycle */
9460 if (DelayReached(&sound_delay, sound_delay_value))
9462 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9463 sound_delay_value = 30;
9467 if (MovDelay[x][y]) /* wait some time before growing bigger */
9470 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9472 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9473 6 - MovDelay[x][y]);
9475 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9478 if (!MovDelay[x][y])
9480 Feld[x][y] = Store[x][y];
9482 TEST_DrawLevelField(x, y);
9487 void AmoebaDisappearing(int x, int y)
9489 static unsigned long sound_delay = 0;
9490 static unsigned long sound_delay_value = 0;
9492 if (!MovDelay[x][y]) /* start new shrinking cycle */
9496 if (DelayReached(&sound_delay, sound_delay_value))
9497 sound_delay_value = 30;
9500 if (MovDelay[x][y]) /* wait some time before shrinking */
9503 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9505 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9506 6 - MovDelay[x][y]);
9508 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9511 if (!MovDelay[x][y])
9513 Feld[x][y] = EL_EMPTY;
9514 TEST_DrawLevelField(x, y);
9516 /* don't let mole enter this field in this cycle;
9517 (give priority to objects falling to this field from above) */
9523 void AmoebeAbleger(int ax, int ay)
9526 int element = Feld[ax][ay];
9527 int graphic = el2img(element);
9528 int newax = ax, neway = ay;
9529 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9530 static int xy[4][2] =
9538 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9540 Feld[ax][ay] = EL_AMOEBA_DEAD;
9541 TEST_DrawLevelField(ax, ay);
9545 if (IS_ANIMATED(graphic))
9546 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9548 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9549 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9551 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9554 if (MovDelay[ax][ay])
9558 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9561 int x = ax + xy[start][0];
9562 int y = ay + xy[start][1];
9564 if (!IN_LEV_FIELD(x, y))
9567 if (IS_FREE(x, y) ||
9568 CAN_GROW_INTO(Feld[x][y]) ||
9569 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9570 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9576 if (newax == ax && neway == ay)
9579 else /* normal or "filled" (BD style) amoeba */
9582 boolean waiting_for_player = FALSE;
9584 for (i = 0; i < NUM_DIRECTIONS; i++)
9586 int j = (start + i) % 4;
9587 int x = ax + xy[j][0];
9588 int y = ay + xy[j][1];
9590 if (!IN_LEV_FIELD(x, y))
9593 if (IS_FREE(x, y) ||
9594 CAN_GROW_INTO(Feld[x][y]) ||
9595 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9596 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9602 else if (IS_PLAYER(x, y))
9603 waiting_for_player = TRUE;
9606 if (newax == ax && neway == ay) /* amoeba cannot grow */
9608 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9610 Feld[ax][ay] = EL_AMOEBA_DEAD;
9611 TEST_DrawLevelField(ax, ay);
9612 AmoebaCnt[AmoebaNr[ax][ay]]--;
9614 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9616 if (element == EL_AMOEBA_FULL)
9617 AmoebeUmwandeln(ax, ay);
9618 else if (element == EL_BD_AMOEBA)
9619 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9624 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9626 /* amoeba gets larger by growing in some direction */
9628 int new_group_nr = AmoebaNr[ax][ay];
9631 if (new_group_nr == 0)
9633 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9634 printf("AmoebeAbleger(): This should never happen!\n");
9639 AmoebaNr[newax][neway] = new_group_nr;
9640 AmoebaCnt[new_group_nr]++;
9641 AmoebaCnt2[new_group_nr]++;
9643 /* if amoeba touches other amoeba(s) after growing, unify them */
9644 AmoebenVereinigen(newax, neway);
9646 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9648 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9654 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9655 (neway == lev_fieldy - 1 && newax != ax))
9657 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9658 Store[newax][neway] = element;
9660 else if (neway == ay || element == EL_EMC_DRIPPER)
9662 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9664 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9668 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9669 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9670 Store[ax][ay] = EL_AMOEBA_DROP;
9671 ContinueMoving(ax, ay);
9675 TEST_DrawLevelField(newax, neway);
9678 void Life(int ax, int ay)
9682 int element = Feld[ax][ay];
9683 int graphic = el2img(element);
9684 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9686 boolean changed = FALSE;
9688 if (IS_ANIMATED(graphic))
9689 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9694 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9695 MovDelay[ax][ay] = life_time;
9697 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9700 if (MovDelay[ax][ay])
9704 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9706 int xx = ax+x1, yy = ay+y1;
9709 if (!IN_LEV_FIELD(xx, yy))
9712 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9714 int x = xx+x2, y = yy+y2;
9716 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9719 if (((Feld[x][y] == element ||
9720 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9722 (IS_FREE(x, y) && Stop[x][y]))
9726 if (xx == ax && yy == ay) /* field in the middle */
9728 if (nachbarn < life_parameter[0] ||
9729 nachbarn > life_parameter[1])
9731 Feld[xx][yy] = EL_EMPTY;
9733 TEST_DrawLevelField(xx, yy);
9734 Stop[xx][yy] = TRUE;
9738 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9739 { /* free border field */
9740 if (nachbarn >= life_parameter[2] &&
9741 nachbarn <= life_parameter[3])
9743 Feld[xx][yy] = element;
9744 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9746 TEST_DrawLevelField(xx, yy);
9747 Stop[xx][yy] = TRUE;
9754 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9755 SND_GAME_OF_LIFE_GROWING);
9758 static void InitRobotWheel(int x, int y)
9760 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9763 static void RunRobotWheel(int x, int y)
9765 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9768 static void StopRobotWheel(int x, int y)
9770 if (ZX == x && ZY == y)
9774 game.robot_wheel_active = FALSE;
9778 static void InitTimegateWheel(int x, int y)
9780 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9783 static void RunTimegateWheel(int x, int y)
9785 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9788 static void InitMagicBallDelay(int x, int y)
9791 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9793 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9797 static void ActivateMagicBall(int bx, int by)
9801 if (level.ball_random)
9803 int pos_border = RND(8); /* select one of the eight border elements */
9804 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9805 int xx = pos_content % 3;
9806 int yy = pos_content / 3;
9811 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9812 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9816 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9818 int xx = x - bx + 1;
9819 int yy = y - by + 1;
9821 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9822 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9826 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9829 void CheckExit(int x, int y)
9831 if (local_player->gems_still_needed > 0 ||
9832 local_player->sokobanfields_still_needed > 0 ||
9833 local_player->lights_still_needed > 0)
9835 int element = Feld[x][y];
9836 int graphic = el2img(element);
9838 if (IS_ANIMATED(graphic))
9839 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9844 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9847 Feld[x][y] = EL_EXIT_OPENING;
9849 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9852 void CheckExitEM(int x, int y)
9854 if (local_player->gems_still_needed > 0 ||
9855 local_player->sokobanfields_still_needed > 0 ||
9856 local_player->lights_still_needed > 0)
9858 int element = Feld[x][y];
9859 int graphic = el2img(element);
9861 if (IS_ANIMATED(graphic))
9862 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9867 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9870 Feld[x][y] = EL_EM_EXIT_OPENING;
9872 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9875 void CheckExitSteel(int x, int y)
9877 if (local_player->gems_still_needed > 0 ||
9878 local_player->sokobanfields_still_needed > 0 ||
9879 local_player->lights_still_needed > 0)
9881 int element = Feld[x][y];
9882 int graphic = el2img(element);
9884 if (IS_ANIMATED(graphic))
9885 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9890 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9893 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9895 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9898 void CheckExitSteelEM(int x, int y)
9900 if (local_player->gems_still_needed > 0 ||
9901 local_player->sokobanfields_still_needed > 0 ||
9902 local_player->lights_still_needed > 0)
9904 int element = Feld[x][y];
9905 int graphic = el2img(element);
9907 if (IS_ANIMATED(graphic))
9908 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9913 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9916 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9918 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9921 void CheckExitSP(int x, int y)
9923 if (local_player->gems_still_needed > 0)
9925 int element = Feld[x][y];
9926 int graphic = el2img(element);
9928 if (IS_ANIMATED(graphic))
9929 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9934 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9937 Feld[x][y] = EL_SP_EXIT_OPENING;
9939 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9942 static void CloseAllOpenTimegates()
9946 SCAN_PLAYFIELD(x, y)
9948 int element = Feld[x][y];
9950 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9952 Feld[x][y] = EL_TIMEGATE_CLOSING;
9954 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9959 void DrawTwinkleOnField(int x, int y)
9961 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9964 if (Feld[x][y] == EL_BD_DIAMOND)
9967 if (MovDelay[x][y] == 0) /* next animation frame */
9968 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9970 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9974 DrawLevelElementAnimation(x, y, Feld[x][y]);
9976 if (MovDelay[x][y] != 0)
9978 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9979 10 - MovDelay[x][y]);
9981 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9986 void MauerWaechst(int x, int y)
9990 if (!MovDelay[x][y]) /* next animation frame */
9991 MovDelay[x][y] = 3 * delay;
9993 if (MovDelay[x][y]) /* wait some time before next frame */
9997 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9999 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10000 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10002 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10005 if (!MovDelay[x][y])
10007 if (MovDir[x][y] == MV_LEFT)
10009 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10010 TEST_DrawLevelField(x - 1, y);
10012 else if (MovDir[x][y] == MV_RIGHT)
10014 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10015 TEST_DrawLevelField(x + 1, y);
10017 else if (MovDir[x][y] == MV_UP)
10019 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10020 TEST_DrawLevelField(x, y - 1);
10024 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10025 TEST_DrawLevelField(x, y + 1);
10028 Feld[x][y] = Store[x][y];
10030 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10031 TEST_DrawLevelField(x, y);
10036 void MauerAbleger(int ax, int ay)
10038 int element = Feld[ax][ay];
10039 int graphic = el2img(element);
10040 boolean oben_frei = FALSE, unten_frei = FALSE;
10041 boolean links_frei = FALSE, rechts_frei = FALSE;
10042 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10043 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10044 boolean new_wall = FALSE;
10046 if (IS_ANIMATED(graphic))
10047 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10049 if (!MovDelay[ax][ay]) /* start building new wall */
10050 MovDelay[ax][ay] = 6;
10052 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10054 MovDelay[ax][ay]--;
10055 if (MovDelay[ax][ay])
10059 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10061 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10063 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10065 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10066 rechts_frei = TRUE;
10068 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10069 element == EL_EXPANDABLE_WALL_ANY)
10073 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10074 Store[ax][ay-1] = element;
10075 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10076 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10077 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10078 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10083 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10084 Store[ax][ay+1] = element;
10085 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10086 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10087 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10088 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10093 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10094 element == EL_EXPANDABLE_WALL_ANY ||
10095 element == EL_EXPANDABLE_WALL ||
10096 element == EL_BD_EXPANDABLE_WALL)
10100 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10101 Store[ax-1][ay] = element;
10102 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10103 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10104 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10105 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10111 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10112 Store[ax+1][ay] = element;
10113 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10114 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10115 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10116 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10121 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10122 TEST_DrawLevelField(ax, ay);
10124 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10125 oben_massiv = TRUE;
10126 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10127 unten_massiv = TRUE;
10128 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10129 links_massiv = TRUE;
10130 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10131 rechts_massiv = TRUE;
10133 if (((oben_massiv && unten_massiv) ||
10134 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10135 element == EL_EXPANDABLE_WALL) &&
10136 ((links_massiv && rechts_massiv) ||
10137 element == EL_EXPANDABLE_WALL_VERTICAL))
10138 Feld[ax][ay] = EL_WALL;
10141 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10144 void MauerAblegerStahl(int ax, int ay)
10146 int element = Feld[ax][ay];
10147 int graphic = el2img(element);
10148 boolean oben_frei = FALSE, unten_frei = FALSE;
10149 boolean links_frei = FALSE, rechts_frei = FALSE;
10150 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10151 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10152 boolean new_wall = FALSE;
10154 if (IS_ANIMATED(graphic))
10155 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10157 if (!MovDelay[ax][ay]) /* start building new wall */
10158 MovDelay[ax][ay] = 6;
10160 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10162 MovDelay[ax][ay]--;
10163 if (MovDelay[ax][ay])
10167 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10169 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10171 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10173 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10174 rechts_frei = TRUE;
10176 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10177 element == EL_EXPANDABLE_STEELWALL_ANY)
10181 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10182 Store[ax][ay-1] = element;
10183 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10184 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10185 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10186 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10191 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10192 Store[ax][ay+1] = element;
10193 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10194 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10195 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10196 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10201 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10202 element == EL_EXPANDABLE_STEELWALL_ANY)
10206 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10207 Store[ax-1][ay] = element;
10208 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10209 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10210 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10211 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10217 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10218 Store[ax+1][ay] = element;
10219 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10220 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10221 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10222 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10227 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10228 oben_massiv = TRUE;
10229 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10230 unten_massiv = TRUE;
10231 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10232 links_massiv = TRUE;
10233 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10234 rechts_massiv = TRUE;
10236 if (((oben_massiv && unten_massiv) ||
10237 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10238 ((links_massiv && rechts_massiv) ||
10239 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10240 Feld[ax][ay] = EL_STEELWALL;
10243 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10246 void CheckForDragon(int x, int y)
10249 boolean dragon_found = FALSE;
10250 static int xy[4][2] =
10258 for (i = 0; i < NUM_DIRECTIONS; i++)
10260 for (j = 0; j < 4; j++)
10262 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10264 if (IN_LEV_FIELD(xx, yy) &&
10265 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10267 if (Feld[xx][yy] == EL_DRAGON)
10268 dragon_found = TRUE;
10277 for (i = 0; i < NUM_DIRECTIONS; i++)
10279 for (j = 0; j < 3; j++)
10281 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10283 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10285 Feld[xx][yy] = EL_EMPTY;
10286 TEST_DrawLevelField(xx, yy);
10295 static void InitBuggyBase(int x, int y)
10297 int element = Feld[x][y];
10298 int activating_delay = FRAMES_PER_SECOND / 4;
10300 ChangeDelay[x][y] =
10301 (element == EL_SP_BUGGY_BASE ?
10302 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10303 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10305 element == EL_SP_BUGGY_BASE_ACTIVE ?
10306 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10309 static void WarnBuggyBase(int x, int y)
10312 static int xy[4][2] =
10320 for (i = 0; i < NUM_DIRECTIONS; i++)
10322 int xx = x + xy[i][0];
10323 int yy = y + xy[i][1];
10325 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10327 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10334 static void InitTrap(int x, int y)
10336 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10339 static void ActivateTrap(int x, int y)
10341 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10344 static void ChangeActiveTrap(int x, int y)
10346 int graphic = IMG_TRAP_ACTIVE;
10348 /* if new animation frame was drawn, correct crumbled sand border */
10349 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10350 TEST_DrawLevelFieldCrumbledSand(x, y);
10353 static int getSpecialActionElement(int element, int number, int base_element)
10355 return (element != EL_EMPTY ? element :
10356 number != -1 ? base_element + number - 1 :
10360 static int getModifiedActionNumber(int value_old, int operator, int operand,
10361 int value_min, int value_max)
10363 int value_new = (operator == CA_MODE_SET ? operand :
10364 operator == CA_MODE_ADD ? value_old + operand :
10365 operator == CA_MODE_SUBTRACT ? value_old - operand :
10366 operator == CA_MODE_MULTIPLY ? value_old * operand :
10367 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10368 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10371 return (value_new < value_min ? value_min :
10372 value_new > value_max ? value_max :
10376 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10378 struct ElementInfo *ei = &element_info[element];
10379 struct ElementChangeInfo *change = &ei->change_page[page];
10380 int target_element = change->target_element;
10381 int action_type = change->action_type;
10382 int action_mode = change->action_mode;
10383 int action_arg = change->action_arg;
10384 int action_element = change->action_element;
10387 if (!change->has_action)
10390 /* ---------- determine action paramater values -------------------------- */
10392 int level_time_value =
10393 (level.time > 0 ? TimeLeft :
10396 int action_arg_element_raw =
10397 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10398 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10399 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10400 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10401 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10402 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10403 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10405 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10408 if (action_arg_element_raw == EL_GROUP_START)
10409 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10412 int action_arg_direction =
10413 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10414 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10415 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10416 change->actual_trigger_side :
10417 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10418 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10421 int action_arg_number_min =
10422 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10425 int action_arg_number_max =
10426 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10427 action_type == CA_SET_LEVEL_GEMS ? 999 :
10428 action_type == CA_SET_LEVEL_TIME ? 9999 :
10429 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10430 action_type == CA_SET_CE_VALUE ? 9999 :
10431 action_type == CA_SET_CE_SCORE ? 9999 :
10434 int action_arg_number_reset =
10435 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10436 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10437 action_type == CA_SET_LEVEL_TIME ? level.time :
10438 action_type == CA_SET_LEVEL_SCORE ? 0 :
10439 #if USE_NEW_CUSTOM_VALUE
10440 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10442 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10444 action_type == CA_SET_CE_SCORE ? 0 :
10447 int action_arg_number =
10448 (action_arg <= CA_ARG_MAX ? action_arg :
10449 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10450 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10451 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10452 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10453 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10454 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10455 #if USE_NEW_CUSTOM_VALUE
10456 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10458 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10460 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10461 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10462 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10463 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10464 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10465 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10466 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10467 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10468 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10469 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10470 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10471 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10472 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10473 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10476 int action_arg_number_old =
10477 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10478 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10479 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10480 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10481 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10484 int action_arg_number_new =
10485 getModifiedActionNumber(action_arg_number_old,
10486 action_mode, action_arg_number,
10487 action_arg_number_min, action_arg_number_max);
10490 int trigger_player_bits =
10491 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10492 change->actual_trigger_player_bits : change->trigger_player);
10494 int trigger_player_bits =
10495 (change->actual_trigger_player >= EL_PLAYER_1 &&
10496 change->actual_trigger_player <= EL_PLAYER_4 ?
10497 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10501 int action_arg_player_bits =
10502 (action_arg >= CA_ARG_PLAYER_1 &&
10503 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10504 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10505 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10508 /* ---------- execute action -------------------------------------------- */
10510 switch (action_type)
10517 /* ---------- level actions ------------------------------------------- */
10519 case CA_RESTART_LEVEL:
10521 game.restart_level = TRUE;
10526 case CA_SHOW_ENVELOPE:
10528 int element = getSpecialActionElement(action_arg_element,
10529 action_arg_number, EL_ENVELOPE_1);
10531 if (IS_ENVELOPE(element))
10532 local_player->show_envelope = element;
10537 case CA_SET_LEVEL_TIME:
10539 if (level.time > 0) /* only modify limited time value */
10541 TimeLeft = action_arg_number_new;
10544 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10546 DisplayGameControlValues();
10548 DrawGameValue_Time(TimeLeft);
10551 if (!TimeLeft && setup.time_limit)
10552 for (i = 0; i < MAX_PLAYERS; i++)
10553 KillPlayer(&stored_player[i]);
10559 case CA_SET_LEVEL_SCORE:
10561 local_player->score = action_arg_number_new;
10564 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10566 DisplayGameControlValues();
10568 DrawGameValue_Score(local_player->score);
10574 case CA_SET_LEVEL_GEMS:
10576 local_player->gems_still_needed = action_arg_number_new;
10579 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10581 DisplayGameControlValues();
10583 DrawGameValue_Emeralds(local_player->gems_still_needed);
10589 #if !USE_PLAYER_GRAVITY
10590 case CA_SET_LEVEL_GRAVITY:
10592 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10593 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10594 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10600 case CA_SET_LEVEL_WIND:
10602 game.wind_direction = action_arg_direction;
10607 case CA_SET_LEVEL_RANDOM_SEED:
10610 /* ensure that setting a new random seed while playing is predictable */
10611 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10613 InitRND(action_arg_number_new);
10617 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10625 for (i = 0; i < 9; i++)
10626 printf("%d, ", RND(2));
10634 /* ---------- player actions ------------------------------------------ */
10636 case CA_MOVE_PLAYER:
10638 /* automatically move to the next field in specified direction */
10639 for (i = 0; i < MAX_PLAYERS; i++)
10640 if (trigger_player_bits & (1 << i))
10641 stored_player[i].programmed_action = action_arg_direction;
10646 case CA_EXIT_PLAYER:
10648 for (i = 0; i < MAX_PLAYERS; i++)
10649 if (action_arg_player_bits & (1 << i))
10650 PlayerWins(&stored_player[i]);
10655 case CA_KILL_PLAYER:
10657 for (i = 0; i < MAX_PLAYERS; i++)
10658 if (action_arg_player_bits & (1 << i))
10659 KillPlayer(&stored_player[i]);
10664 case CA_SET_PLAYER_KEYS:
10666 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10667 int element = getSpecialActionElement(action_arg_element,
10668 action_arg_number, EL_KEY_1);
10670 if (IS_KEY(element))
10672 for (i = 0; i < MAX_PLAYERS; i++)
10674 if (trigger_player_bits & (1 << i))
10676 stored_player[i].key[KEY_NR(element)] = key_state;
10678 DrawGameDoorValues();
10686 case CA_SET_PLAYER_SPEED:
10689 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10692 for (i = 0; i < MAX_PLAYERS; i++)
10694 if (trigger_player_bits & (1 << i))
10696 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10698 if (action_arg == CA_ARG_SPEED_FASTER &&
10699 stored_player[i].cannot_move)
10701 action_arg_number = STEPSIZE_VERY_SLOW;
10703 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10704 action_arg == CA_ARG_SPEED_FASTER)
10706 action_arg_number = 2;
10707 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10710 else if (action_arg == CA_ARG_NUMBER_RESET)
10712 action_arg_number = level.initial_player_stepsize[i];
10716 getModifiedActionNumber(move_stepsize,
10719 action_arg_number_min,
10720 action_arg_number_max);
10722 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10729 case CA_SET_PLAYER_SHIELD:
10731 for (i = 0; i < MAX_PLAYERS; i++)
10733 if (trigger_player_bits & (1 << i))
10735 if (action_arg == CA_ARG_SHIELD_OFF)
10737 stored_player[i].shield_normal_time_left = 0;
10738 stored_player[i].shield_deadly_time_left = 0;
10740 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10742 stored_player[i].shield_normal_time_left = 999999;
10744 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10746 stored_player[i].shield_normal_time_left = 999999;
10747 stored_player[i].shield_deadly_time_left = 999999;
10755 #if USE_PLAYER_GRAVITY
10756 case CA_SET_PLAYER_GRAVITY:
10758 for (i = 0; i < MAX_PLAYERS; i++)
10760 if (trigger_player_bits & (1 << i))
10762 stored_player[i].gravity =
10763 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10764 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10765 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10766 stored_player[i].gravity);
10774 case CA_SET_PLAYER_ARTWORK:
10776 for (i = 0; i < MAX_PLAYERS; i++)
10778 if (trigger_player_bits & (1 << i))
10780 int artwork_element = action_arg_element;
10782 if (action_arg == CA_ARG_ELEMENT_RESET)
10784 (level.use_artwork_element[i] ? level.artwork_element[i] :
10785 stored_player[i].element_nr);
10787 #if USE_GFX_RESET_PLAYER_ARTWORK
10788 if (stored_player[i].artwork_element != artwork_element)
10789 stored_player[i].Frame = 0;
10792 stored_player[i].artwork_element = artwork_element;
10794 SetPlayerWaiting(&stored_player[i], FALSE);
10796 /* set number of special actions for bored and sleeping animation */
10797 stored_player[i].num_special_action_bored =
10798 get_num_special_action(artwork_element,
10799 ACTION_BORING_1, ACTION_BORING_LAST);
10800 stored_player[i].num_special_action_sleeping =
10801 get_num_special_action(artwork_element,
10802 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10809 case CA_SET_PLAYER_INVENTORY:
10811 for (i = 0; i < MAX_PLAYERS; i++)
10813 struct PlayerInfo *player = &stored_player[i];
10816 if (trigger_player_bits & (1 << i))
10818 int inventory_element = action_arg_element;
10820 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10821 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10822 action_arg == CA_ARG_ELEMENT_ACTION)
10824 int element = inventory_element;
10825 int collect_count = element_info[element].collect_count_initial;
10827 if (!IS_CUSTOM_ELEMENT(element))
10830 if (collect_count == 0)
10831 player->inventory_infinite_element = element;
10833 for (k = 0; k < collect_count; k++)
10834 if (player->inventory_size < MAX_INVENTORY_SIZE)
10835 player->inventory_element[player->inventory_size++] =
10838 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10839 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10840 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10842 if (player->inventory_infinite_element != EL_UNDEFINED &&
10843 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10844 action_arg_element_raw))
10845 player->inventory_infinite_element = EL_UNDEFINED;
10847 for (k = 0, j = 0; j < player->inventory_size; j++)
10849 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10850 action_arg_element_raw))
10851 player->inventory_element[k++] = player->inventory_element[j];
10854 player->inventory_size = k;
10856 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10858 if (player->inventory_size > 0)
10860 for (j = 0; j < player->inventory_size - 1; j++)
10861 player->inventory_element[j] = player->inventory_element[j + 1];
10863 player->inventory_size--;
10866 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10868 if (player->inventory_size > 0)
10869 player->inventory_size--;
10871 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10873 player->inventory_infinite_element = EL_UNDEFINED;
10874 player->inventory_size = 0;
10876 else if (action_arg == CA_ARG_INVENTORY_RESET)
10878 player->inventory_infinite_element = EL_UNDEFINED;
10879 player->inventory_size = 0;
10881 if (level.use_initial_inventory[i])
10883 for (j = 0; j < level.initial_inventory_size[i]; j++)
10885 int element = level.initial_inventory_content[i][j];
10886 int collect_count = element_info[element].collect_count_initial;
10888 if (!IS_CUSTOM_ELEMENT(element))
10891 if (collect_count == 0)
10892 player->inventory_infinite_element = element;
10894 for (k = 0; k < collect_count; k++)
10895 if (player->inventory_size < MAX_INVENTORY_SIZE)
10896 player->inventory_element[player->inventory_size++] =
10907 /* ---------- CE actions ---------------------------------------------- */
10909 case CA_SET_CE_VALUE:
10911 #if USE_NEW_CUSTOM_VALUE
10912 int last_ce_value = CustomValue[x][y];
10914 CustomValue[x][y] = action_arg_number_new;
10916 if (CustomValue[x][y] != last_ce_value)
10918 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10919 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10921 if (CustomValue[x][y] == 0)
10923 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10924 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10932 case CA_SET_CE_SCORE:
10934 #if USE_NEW_CUSTOM_VALUE
10935 int last_ce_score = ei->collect_score;
10937 ei->collect_score = action_arg_number_new;
10939 if (ei->collect_score != last_ce_score)
10941 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10942 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10944 if (ei->collect_score == 0)
10948 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10949 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10952 This is a very special case that seems to be a mixture between
10953 CheckElementChange() and CheckTriggeredElementChange(): while
10954 the first one only affects single elements that are triggered
10955 directly, the second one affects multiple elements in the playfield
10956 that are triggered indirectly by another element. This is a third
10957 case: Changing the CE score always affects multiple identical CEs,
10958 so every affected CE must be checked, not only the single CE for
10959 which the CE score was changed in the first place (as every instance
10960 of that CE shares the same CE score, and therefore also can change)!
10962 SCAN_PLAYFIELD(xx, yy)
10964 if (Feld[xx][yy] == element)
10965 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10966 CE_SCORE_GETS_ZERO);
10975 case CA_SET_CE_ARTWORK:
10977 int artwork_element = action_arg_element;
10978 boolean reset_frame = FALSE;
10981 if (action_arg == CA_ARG_ELEMENT_RESET)
10982 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10985 if (ei->gfx_element != artwork_element)
10986 reset_frame = TRUE;
10988 ei->gfx_element = artwork_element;
10990 SCAN_PLAYFIELD(xx, yy)
10992 if (Feld[xx][yy] == element)
10996 ResetGfxAnimation(xx, yy);
10997 ResetRandomAnimationValue(xx, yy);
11000 TEST_DrawLevelField(xx, yy);
11007 /* ---------- engine actions ------------------------------------------ */
11009 case CA_SET_ENGINE_SCAN_MODE:
11011 InitPlayfieldScanMode(action_arg);
11021 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11023 int old_element = Feld[x][y];
11024 int new_element = GetElementFromGroupElement(element);
11025 int previous_move_direction = MovDir[x][y];
11026 #if USE_NEW_CUSTOM_VALUE
11027 int last_ce_value = CustomValue[x][y];
11029 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11030 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11031 boolean add_player_onto_element = (new_element_is_player &&
11032 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11033 /* this breaks SnakeBite when a snake is
11034 halfway through a door that closes */
11035 /* NOW FIXED AT LEVEL INIT IN files.c */
11036 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11038 IS_WALKABLE(old_element));
11041 /* check if element under the player changes from accessible to unaccessible
11042 (needed for special case of dropping element which then changes) */
11043 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11044 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11052 if (!add_player_onto_element)
11054 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11055 RemoveMovingField(x, y);
11059 Feld[x][y] = new_element;
11061 #if !USE_GFX_RESET_GFX_ANIMATION
11062 ResetGfxAnimation(x, y);
11063 ResetRandomAnimationValue(x, y);
11066 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11067 MovDir[x][y] = previous_move_direction;
11069 #if USE_NEW_CUSTOM_VALUE
11070 if (element_info[new_element].use_last_ce_value)
11071 CustomValue[x][y] = last_ce_value;
11074 InitField_WithBug1(x, y, FALSE);
11076 new_element = Feld[x][y]; /* element may have changed */
11078 #if USE_GFX_RESET_GFX_ANIMATION
11079 ResetGfxAnimation(x, y);
11080 ResetRandomAnimationValue(x, y);
11083 TEST_DrawLevelField(x, y);
11085 if (GFX_CRUMBLED(new_element))
11086 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11090 /* check if element under the player changes from accessible to unaccessible
11091 (needed for special case of dropping element which then changes) */
11092 /* (must be checked after creating new element for walkable group elements) */
11093 #if USE_FIX_KILLED_BY_NON_WALKABLE
11094 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11095 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11102 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11103 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11112 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11113 if (new_element_is_player)
11114 RelocatePlayer(x, y, new_element);
11117 ChangeCount[x][y]++; /* count number of changes in the same frame */
11119 TestIfBadThingTouchesPlayer(x, y);
11120 TestIfPlayerTouchesCustomElement(x, y);
11121 TestIfElementTouchesCustomElement(x, y);
11124 static void CreateField(int x, int y, int element)
11126 CreateFieldExt(x, y, element, FALSE);
11129 static void CreateElementFromChange(int x, int y, int element)
11131 element = GET_VALID_RUNTIME_ELEMENT(element);
11133 #if USE_STOP_CHANGED_ELEMENTS
11134 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11136 int old_element = Feld[x][y];
11138 /* prevent changed element from moving in same engine frame
11139 unless both old and new element can either fall or move */
11140 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11141 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11146 CreateFieldExt(x, y, element, TRUE);
11149 static boolean ChangeElement(int x, int y, int element, int page)
11151 struct ElementInfo *ei = &element_info[element];
11152 struct ElementChangeInfo *change = &ei->change_page[page];
11153 int ce_value = CustomValue[x][y];
11154 int ce_score = ei->collect_score;
11155 int target_element;
11156 int old_element = Feld[x][y];
11158 /* always use default change event to prevent running into a loop */
11159 if (ChangeEvent[x][y] == -1)
11160 ChangeEvent[x][y] = CE_DELAY;
11162 if (ChangeEvent[x][y] == CE_DELAY)
11164 /* reset actual trigger element, trigger player and action element */
11165 change->actual_trigger_element = EL_EMPTY;
11166 change->actual_trigger_player = EL_EMPTY;
11167 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11168 change->actual_trigger_side = CH_SIDE_NONE;
11169 change->actual_trigger_ce_value = 0;
11170 change->actual_trigger_ce_score = 0;
11173 /* do not change elements more than a specified maximum number of changes */
11174 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11177 ChangeCount[x][y]++; /* count number of changes in the same frame */
11179 if (change->explode)
11186 if (change->use_target_content)
11188 boolean complete_replace = TRUE;
11189 boolean can_replace[3][3];
11192 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11195 boolean is_walkable;
11196 boolean is_diggable;
11197 boolean is_collectible;
11198 boolean is_removable;
11199 boolean is_destructible;
11200 int ex = x + xx - 1;
11201 int ey = y + yy - 1;
11202 int content_element = change->target_content.e[xx][yy];
11205 can_replace[xx][yy] = TRUE;
11207 if (ex == x && ey == y) /* do not check changing element itself */
11210 if (content_element == EL_EMPTY_SPACE)
11212 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11217 if (!IN_LEV_FIELD(ex, ey))
11219 can_replace[xx][yy] = FALSE;
11220 complete_replace = FALSE;
11227 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11228 e = MovingOrBlocked2Element(ex, ey);
11230 is_empty = (IS_FREE(ex, ey) ||
11231 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11233 is_walkable = (is_empty || IS_WALKABLE(e));
11234 is_diggable = (is_empty || IS_DIGGABLE(e));
11235 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11236 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11237 is_removable = (is_diggable || is_collectible);
11239 can_replace[xx][yy] =
11240 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11241 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11242 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11243 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11244 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11245 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11246 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11248 if (!can_replace[xx][yy])
11249 complete_replace = FALSE;
11252 if (!change->only_if_complete || complete_replace)
11254 boolean something_has_changed = FALSE;
11256 if (change->only_if_complete && change->use_random_replace &&
11257 RND(100) < change->random_percentage)
11260 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11262 int ex = x + xx - 1;
11263 int ey = y + yy - 1;
11264 int content_element;
11266 if (can_replace[xx][yy] && (!change->use_random_replace ||
11267 RND(100) < change->random_percentage))
11269 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11270 RemoveMovingField(ex, ey);
11272 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11274 content_element = change->target_content.e[xx][yy];
11275 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11276 ce_value, ce_score);
11278 CreateElementFromChange(ex, ey, target_element);
11280 something_has_changed = TRUE;
11282 /* for symmetry reasons, freeze newly created border elements */
11283 if (ex != x || ey != y)
11284 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11288 if (something_has_changed)
11290 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11291 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11297 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11298 ce_value, ce_score);
11300 if (element == EL_DIAGONAL_GROWING ||
11301 element == EL_DIAGONAL_SHRINKING)
11303 target_element = Store[x][y];
11305 Store[x][y] = EL_EMPTY;
11308 CreateElementFromChange(x, y, target_element);
11310 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11311 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11314 /* this uses direct change before indirect change */
11315 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11320 #if USE_NEW_DELAYED_ACTION
11322 static void HandleElementChange(int x, int y, int page)
11324 int element = MovingOrBlocked2Element(x, y);
11325 struct ElementInfo *ei = &element_info[element];
11326 struct ElementChangeInfo *change = &ei->change_page[page];
11327 boolean handle_action_before_change = FALSE;
11330 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11331 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11334 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11335 x, y, element, element_info[element].token_name);
11336 printf("HandleElementChange(): This should never happen!\n");
11341 /* this can happen with classic bombs on walkable, changing elements */
11342 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11345 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11346 ChangeDelay[x][y] = 0;
11352 if (ChangeDelay[x][y] == 0) /* initialize element change */
11354 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11356 if (change->can_change)
11359 /* !!! not clear why graphic animation should be reset at all here !!! */
11360 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11361 #if USE_GFX_RESET_WHEN_NOT_MOVING
11362 /* when a custom element is about to change (for example by change delay),
11363 do not reset graphic animation when the custom element is moving */
11364 if (!IS_MOVING(x, y))
11367 ResetGfxAnimation(x, y);
11368 ResetRandomAnimationValue(x, y);
11372 if (change->pre_change_function)
11373 change->pre_change_function(x, y);
11377 ChangeDelay[x][y]--;
11379 if (ChangeDelay[x][y] != 0) /* continue element change */
11381 if (change->can_change)
11383 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11385 if (IS_ANIMATED(graphic))
11386 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11388 if (change->change_function)
11389 change->change_function(x, y);
11392 else /* finish element change */
11394 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11396 page = ChangePage[x][y];
11397 ChangePage[x][y] = -1;
11399 change = &ei->change_page[page];
11402 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11404 ChangeDelay[x][y] = 1; /* try change after next move step */
11405 ChangePage[x][y] = page; /* remember page to use for change */
11411 /* special case: set new level random seed before changing element */
11412 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11413 handle_action_before_change = TRUE;
11415 if (change->has_action && handle_action_before_change)
11416 ExecuteCustomElementAction(x, y, element, page);
11419 if (change->can_change)
11421 if (ChangeElement(x, y, element, page))
11423 if (change->post_change_function)
11424 change->post_change_function(x, y);
11428 if (change->has_action && !handle_action_before_change)
11429 ExecuteCustomElementAction(x, y, element, page);
11435 static void HandleElementChange(int x, int y, int page)
11437 int element = MovingOrBlocked2Element(x, y);
11438 struct ElementInfo *ei = &element_info[element];
11439 struct ElementChangeInfo *change = &ei->change_page[page];
11442 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11445 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11446 x, y, element, element_info[element].token_name);
11447 printf("HandleElementChange(): This should never happen!\n");
11452 /* this can happen with classic bombs on walkable, changing elements */
11453 if (!CAN_CHANGE(element))
11456 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11457 ChangeDelay[x][y] = 0;
11463 if (ChangeDelay[x][y] == 0) /* initialize element change */
11465 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11467 ResetGfxAnimation(x, y);
11468 ResetRandomAnimationValue(x, y);
11470 if (change->pre_change_function)
11471 change->pre_change_function(x, y);
11474 ChangeDelay[x][y]--;
11476 if (ChangeDelay[x][y] != 0) /* continue element change */
11478 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11480 if (IS_ANIMATED(graphic))
11481 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11483 if (change->change_function)
11484 change->change_function(x, y);
11486 else /* finish element change */
11488 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11490 page = ChangePage[x][y];
11491 ChangePage[x][y] = -1;
11493 change = &ei->change_page[page];
11496 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11498 ChangeDelay[x][y] = 1; /* try change after next move step */
11499 ChangePage[x][y] = page; /* remember page to use for change */
11504 if (ChangeElement(x, y, element, page))
11506 if (change->post_change_function)
11507 change->post_change_function(x, y);
11514 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11515 int trigger_element,
11517 int trigger_player,
11521 boolean change_done_any = FALSE;
11522 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11525 if (!(trigger_events[trigger_element][trigger_event]))
11529 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11530 trigger_event, recursion_loop_depth, recursion_loop_detected,
11531 recursion_loop_element, EL_NAME(recursion_loop_element));
11534 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11536 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11538 int element = EL_CUSTOM_START + i;
11539 boolean change_done = FALSE;
11542 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11543 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11546 for (p = 0; p < element_info[element].num_change_pages; p++)
11548 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11550 if (change->can_change_or_has_action &&
11551 change->has_event[trigger_event] &&
11552 change->trigger_side & trigger_side &&
11553 change->trigger_player & trigger_player &&
11554 change->trigger_page & trigger_page_bits &&
11555 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11557 change->actual_trigger_element = trigger_element;
11558 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11559 change->actual_trigger_player_bits = trigger_player;
11560 change->actual_trigger_side = trigger_side;
11561 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11562 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11565 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11566 element, EL_NAME(element), p);
11569 if ((change->can_change && !change_done) || change->has_action)
11573 SCAN_PLAYFIELD(x, y)
11575 if (Feld[x][y] == element)
11577 if (change->can_change && !change_done)
11579 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11580 /* if element already changed in this frame, not only prevent
11581 another element change (checked in ChangeElement()), but
11582 also prevent additional element actions for this element */
11584 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11585 !level.use_action_after_change_bug)
11590 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11591 element, EL_NAME(element), p);
11594 ChangeDelay[x][y] = 1;
11595 ChangeEvent[x][y] = trigger_event;
11597 HandleElementChange(x, y, p);
11599 #if USE_NEW_DELAYED_ACTION
11600 else if (change->has_action)
11602 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11603 /* if element already changed in this frame, not only prevent
11604 another element change (checked in ChangeElement()), but
11605 also prevent additional element actions for this element */
11607 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11608 !level.use_action_after_change_bug)
11614 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11615 element, EL_NAME(element), p);
11618 ExecuteCustomElementAction(x, y, element, p);
11619 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11622 if (change->has_action)
11624 ExecuteCustomElementAction(x, y, element, p);
11625 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11631 if (change->can_change)
11633 change_done = TRUE;
11634 change_done_any = TRUE;
11637 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11638 element, EL_NAME(element), p);
11647 RECURSION_LOOP_DETECTION_END();
11649 return change_done_any;
11652 static boolean CheckElementChangeExt(int x, int y,
11654 int trigger_element,
11656 int trigger_player,
11659 boolean change_done = FALSE;
11662 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11663 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11666 if (Feld[x][y] == EL_BLOCKED)
11668 Blocked2Moving(x, y, &x, &y);
11669 element = Feld[x][y];
11673 /* check if element has already changed */
11674 if (Feld[x][y] != element)
11677 /* check if element has already changed or is about to change after moving */
11678 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11679 Feld[x][y] != element) ||
11681 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11682 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11683 ChangePage[x][y] != -1)))
11688 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11689 trigger_event, recursion_loop_depth, recursion_loop_detected,
11690 recursion_loop_element, EL_NAME(recursion_loop_element));
11693 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11696 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11699 for (p = 0; p < element_info[element].num_change_pages; p++)
11701 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11703 /* check trigger element for all events where the element that is checked
11704 for changing interacts with a directly adjacent element -- this is
11705 different to element changes that affect other elements to change on the
11706 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11707 boolean check_trigger_element =
11708 (trigger_event == CE_TOUCHING_X ||
11709 trigger_event == CE_HITTING_X ||
11710 trigger_event == CE_HIT_BY_X ||
11712 /* this one was forgotten until 3.2.3 */
11713 trigger_event == CE_DIGGING_X);
11716 if (change->can_change_or_has_action &&
11717 change->has_event[trigger_event] &&
11718 change->trigger_side & trigger_side &&
11719 change->trigger_player & trigger_player &&
11720 (!check_trigger_element ||
11721 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11723 change->actual_trigger_element = trigger_element;
11724 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11725 change->actual_trigger_player_bits = trigger_player;
11726 change->actual_trigger_side = trigger_side;
11727 change->actual_trigger_ce_value = CustomValue[x][y];
11728 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11730 /* special case: trigger element not at (x,y) position for some events */
11731 if (check_trigger_element)
11743 { 0, 0 }, { 0, 0 }, { 0, 0 },
11747 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11748 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11750 change->actual_trigger_ce_value = CustomValue[xx][yy];
11751 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11754 if (change->can_change && !change_done)
11756 ChangeDelay[x][y] = 1;
11757 ChangeEvent[x][y] = trigger_event;
11759 HandleElementChange(x, y, p);
11761 change_done = TRUE;
11763 #if USE_NEW_DELAYED_ACTION
11764 else if (change->has_action)
11766 ExecuteCustomElementAction(x, y, element, p);
11767 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11770 if (change->has_action)
11772 ExecuteCustomElementAction(x, y, element, p);
11773 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11779 RECURSION_LOOP_DETECTION_END();
11781 return change_done;
11784 static void PlayPlayerSound(struct PlayerInfo *player)
11786 int jx = player->jx, jy = player->jy;
11787 int sound_element = player->artwork_element;
11788 int last_action = player->last_action_waiting;
11789 int action = player->action_waiting;
11791 if (player->is_waiting)
11793 if (action != last_action)
11794 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11796 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11800 if (action != last_action)
11801 StopSound(element_info[sound_element].sound[last_action]);
11803 if (last_action == ACTION_SLEEPING)
11804 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11808 static void PlayAllPlayersSound()
11812 for (i = 0; i < MAX_PLAYERS; i++)
11813 if (stored_player[i].active)
11814 PlayPlayerSound(&stored_player[i]);
11817 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11819 boolean last_waiting = player->is_waiting;
11820 int move_dir = player->MovDir;
11822 player->dir_waiting = move_dir;
11823 player->last_action_waiting = player->action_waiting;
11827 if (!last_waiting) /* not waiting -> waiting */
11829 player->is_waiting = TRUE;
11831 player->frame_counter_bored =
11833 game.player_boring_delay_fixed +
11834 GetSimpleRandom(game.player_boring_delay_random);
11835 player->frame_counter_sleeping =
11837 game.player_sleeping_delay_fixed +
11838 GetSimpleRandom(game.player_sleeping_delay_random);
11840 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11843 if (game.player_sleeping_delay_fixed +
11844 game.player_sleeping_delay_random > 0 &&
11845 player->anim_delay_counter == 0 &&
11846 player->post_delay_counter == 0 &&
11847 FrameCounter >= player->frame_counter_sleeping)
11848 player->is_sleeping = TRUE;
11849 else if (game.player_boring_delay_fixed +
11850 game.player_boring_delay_random > 0 &&
11851 FrameCounter >= player->frame_counter_bored)
11852 player->is_bored = TRUE;
11854 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11855 player->is_bored ? ACTION_BORING :
11858 if (player->is_sleeping && player->use_murphy)
11860 /* special case for sleeping Murphy when leaning against non-free tile */
11862 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11863 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11864 !IS_MOVING(player->jx - 1, player->jy)))
11865 move_dir = MV_LEFT;
11866 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11867 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11868 !IS_MOVING(player->jx + 1, player->jy)))
11869 move_dir = MV_RIGHT;
11871 player->is_sleeping = FALSE;
11873 player->dir_waiting = move_dir;
11876 if (player->is_sleeping)
11878 if (player->num_special_action_sleeping > 0)
11880 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11882 int last_special_action = player->special_action_sleeping;
11883 int num_special_action = player->num_special_action_sleeping;
11884 int special_action =
11885 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11886 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11887 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11888 last_special_action + 1 : ACTION_SLEEPING);
11889 int special_graphic =
11890 el_act_dir2img(player->artwork_element, special_action, move_dir);
11892 player->anim_delay_counter =
11893 graphic_info[special_graphic].anim_delay_fixed +
11894 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11895 player->post_delay_counter =
11896 graphic_info[special_graphic].post_delay_fixed +
11897 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11899 player->special_action_sleeping = special_action;
11902 if (player->anim_delay_counter > 0)
11904 player->action_waiting = player->special_action_sleeping;
11905 player->anim_delay_counter--;
11907 else if (player->post_delay_counter > 0)
11909 player->post_delay_counter--;
11913 else if (player->is_bored)
11915 if (player->num_special_action_bored > 0)
11917 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11919 int special_action =
11920 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11921 int special_graphic =
11922 el_act_dir2img(player->artwork_element, special_action, move_dir);
11924 player->anim_delay_counter =
11925 graphic_info[special_graphic].anim_delay_fixed +
11926 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11927 player->post_delay_counter =
11928 graphic_info[special_graphic].post_delay_fixed +
11929 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11931 player->special_action_bored = special_action;
11934 if (player->anim_delay_counter > 0)
11936 player->action_waiting = player->special_action_bored;
11937 player->anim_delay_counter--;
11939 else if (player->post_delay_counter > 0)
11941 player->post_delay_counter--;
11946 else if (last_waiting) /* waiting -> not waiting */
11948 player->is_waiting = FALSE;
11949 player->is_bored = FALSE;
11950 player->is_sleeping = FALSE;
11952 player->frame_counter_bored = -1;
11953 player->frame_counter_sleeping = -1;
11955 player->anim_delay_counter = 0;
11956 player->post_delay_counter = 0;
11958 player->dir_waiting = player->MovDir;
11959 player->action_waiting = ACTION_DEFAULT;
11961 player->special_action_bored = ACTION_DEFAULT;
11962 player->special_action_sleeping = ACTION_DEFAULT;
11966 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11968 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11969 int left = player_action & JOY_LEFT;
11970 int right = player_action & JOY_RIGHT;
11971 int up = player_action & JOY_UP;
11972 int down = player_action & JOY_DOWN;
11973 int button1 = player_action & JOY_BUTTON_1;
11974 int button2 = player_action & JOY_BUTTON_2;
11975 int dx = (left ? -1 : right ? 1 : 0);
11976 int dy = (up ? -1 : down ? 1 : 0);
11978 if (!player->active || tape.pausing)
11984 snapped = SnapField(player, dx, dy);
11988 dropped = DropElement(player);
11990 moved = MovePlayer(player, dx, dy);
11993 if (tape.single_step && tape.recording && !tape.pausing)
11996 /* as it is called "single step mode", just return to pause mode when the
11997 player stopped moving after one tile (or never starts moving at all) */
11998 if (!player->is_moving)
12000 /* this is buggy: there are quite some cases where the single step mode
12001 does not return to pause mode (like pushing things that don't move
12002 or simply by trying to run against a wall) */
12003 if (button1 || (dropped && !moved))
12006 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12007 SnapField(player, 0, 0); /* stop snapping */
12011 SetPlayerWaiting(player, FALSE);
12013 return player_action;
12017 /* no actions for this player (no input at player's configured device) */
12019 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12020 SnapField(player, 0, 0);
12021 CheckGravityMovementWhenNotMoving(player);
12023 if (player->MovPos == 0)
12024 SetPlayerWaiting(player, TRUE);
12026 if (player->MovPos == 0) /* needed for tape.playing */
12027 player->is_moving = FALSE;
12029 player->is_dropping = FALSE;
12030 player->is_dropping_pressed = FALSE;
12031 player->drop_pressed_delay = 0;
12037 static void CheckLevelTime()
12041 /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12042 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12044 if (level.native_em_level->lev->home == 0) /* all players at home */
12046 PlayerWins(local_player);
12048 AllPlayersGone = TRUE;
12050 level.native_em_level->lev->home = -1;
12053 if (level.native_em_level->ply[0]->alive == 0 &&
12054 level.native_em_level->ply[1]->alive == 0 &&
12055 level.native_em_level->ply[2]->alive == 0 &&
12056 level.native_em_level->ply[3]->alive == 0) /* all dead */
12057 AllPlayersGone = TRUE;
12059 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12061 if (game_sp.LevelSolved &&
12062 !game_sp.GameOver) /* game won */
12064 PlayerWins(local_player);
12066 game_sp.GameOver = TRUE;
12068 AllPlayersGone = TRUE;
12071 if (game_sp.GameOver) /* game lost */
12072 AllPlayersGone = TRUE;
12075 if (TimeFrames >= FRAMES_PER_SECOND)
12080 for (i = 0; i < MAX_PLAYERS; i++)
12082 struct PlayerInfo *player = &stored_player[i];
12084 if (SHIELD_ON(player))
12086 player->shield_normal_time_left--;
12088 if (player->shield_deadly_time_left > 0)
12089 player->shield_deadly_time_left--;
12093 if (!local_player->LevelSolved && !level.use_step_counter)
12101 if (TimeLeft <= 10 && setup.time_limit)
12102 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12105 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12107 DisplayGameControlValues();
12109 DrawGameValue_Time(TimeLeft);
12112 if (!TimeLeft && setup.time_limit)
12114 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12115 level.native_em_level->lev->killed_out_of_time = TRUE;
12117 for (i = 0; i < MAX_PLAYERS; i++)
12118 KillPlayer(&stored_player[i]);
12122 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12124 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12126 DisplayGameControlValues();
12129 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12130 DrawGameValue_Time(TimePlayed);
12133 level.native_em_level->lev->time =
12134 (level.time == 0 ? TimePlayed : TimeLeft);
12137 if (tape.recording || tape.playing)
12138 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12142 UpdateAndDisplayGameControlValues();
12144 UpdateGameDoorValues();
12145 DrawGameDoorValues();
12149 void AdvanceFrameAndPlayerCounters(int player_nr)
12153 /* advance frame counters (global frame counter and time frame counter) */
12157 /* advance player counters (counters for move delay, move animation etc.) */
12158 for (i = 0; i < MAX_PLAYERS; i++)
12160 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12161 int move_delay_value = stored_player[i].move_delay_value;
12162 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12164 if (!advance_player_counters) /* not all players may be affected */
12167 #if USE_NEW_PLAYER_ANIM
12168 if (move_frames == 0) /* less than one move per game frame */
12170 int stepsize = TILEX / move_delay_value;
12171 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12172 int count = (stored_player[i].is_moving ?
12173 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12175 if (count % delay == 0)
12180 stored_player[i].Frame += move_frames;
12182 if (stored_player[i].MovPos != 0)
12183 stored_player[i].StepFrame += move_frames;
12185 if (stored_player[i].move_delay > 0)
12186 stored_player[i].move_delay--;
12188 /* due to bugs in previous versions, counter must count up, not down */
12189 if (stored_player[i].push_delay != -1)
12190 stored_player[i].push_delay++;
12192 if (stored_player[i].drop_delay > 0)
12193 stored_player[i].drop_delay--;
12195 if (stored_player[i].is_dropping_pressed)
12196 stored_player[i].drop_pressed_delay++;
12200 void StartGameActions(boolean init_network_game, boolean record_tape,
12203 unsigned long new_random_seed = InitRND(random_seed);
12206 TapeStartRecording(new_random_seed);
12208 #if defined(NETWORK_AVALIABLE)
12209 if (init_network_game)
12211 SendToServer_StartPlaying();
12222 static unsigned long game_frame_delay = 0;
12223 unsigned long game_frame_delay_value;
12224 byte *recorded_player_action;
12225 byte summarized_player_action = 0;
12226 byte tape_action[MAX_PLAYERS];
12229 /* detect endless loops, caused by custom element programming */
12230 if (recursion_loop_detected && recursion_loop_depth == 0)
12232 char *message = getStringCat3("Internal Error ! Element ",
12233 EL_NAME(recursion_loop_element),
12234 " caused endless loop ! Quit the game ?");
12236 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12237 EL_NAME(recursion_loop_element));
12239 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12241 recursion_loop_detected = FALSE; /* if game should be continued */
12248 if (game.restart_level)
12249 StartGameActions(options.network, setup.autorecord, level.random_seed);
12251 /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12252 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12254 if (level.native_em_level->lev->home == 0) /* all players at home */
12256 PlayerWins(local_player);
12258 AllPlayersGone = TRUE;
12260 level.native_em_level->lev->home = -1;
12263 if (level.native_em_level->ply[0]->alive == 0 &&
12264 level.native_em_level->ply[1]->alive == 0 &&
12265 level.native_em_level->ply[2]->alive == 0 &&
12266 level.native_em_level->ply[3]->alive == 0) /* all dead */
12267 AllPlayersGone = TRUE;
12269 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12271 if (game_sp.LevelSolved &&
12272 !game_sp.GameOver) /* game won */
12274 PlayerWins(local_player);
12276 game_sp.GameOver = TRUE;
12278 AllPlayersGone = TRUE;
12281 if (game_sp.GameOver) /* game lost */
12282 AllPlayersGone = TRUE;
12285 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12288 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12291 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12294 game_frame_delay_value =
12295 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12297 if (tape.playing && tape.warp_forward && !tape.pausing)
12298 game_frame_delay_value = 0;
12300 /* ---------- main game synchronization point ---------- */
12302 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12304 if (network_playing && !network_player_action_received)
12306 /* try to get network player actions in time */
12308 #if defined(NETWORK_AVALIABLE)
12309 /* last chance to get network player actions without main loop delay */
12310 HandleNetworking();
12313 /* game was quit by network peer */
12314 if (game_status != GAME_MODE_PLAYING)
12317 if (!network_player_action_received)
12318 return; /* failed to get network player actions in time */
12320 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12326 /* at this point we know that we really continue executing the game */
12328 network_player_action_received = FALSE;
12330 /* when playing tape, read previously recorded player input from tape data */
12331 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12334 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12339 if (tape.set_centered_player)
12341 game.centered_player_nr_next = tape.centered_player_nr_next;
12342 game.set_centered_player = TRUE;
12345 for (i = 0; i < MAX_PLAYERS; i++)
12347 summarized_player_action |= stored_player[i].action;
12349 if (!network_playing)
12350 stored_player[i].effective_action = stored_player[i].action;
12353 #if defined(NETWORK_AVALIABLE)
12354 if (network_playing)
12355 SendToServer_MovePlayer(summarized_player_action);
12358 if (!options.network && !setup.team_mode)
12359 local_player->effective_action = summarized_player_action;
12361 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12363 for (i = 0; i < MAX_PLAYERS; i++)
12364 stored_player[i].effective_action =
12365 (i == game.centered_player_nr ? summarized_player_action : 0);
12368 if (recorded_player_action != NULL)
12369 for (i = 0; i < MAX_PLAYERS; i++)
12370 stored_player[i].effective_action = recorded_player_action[i];
12372 for (i = 0; i < MAX_PLAYERS; i++)
12374 tape_action[i] = stored_player[i].effective_action;
12376 /* (this can only happen in the R'n'D game engine) */
12377 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12378 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12381 /* only record actions from input devices, but not programmed actions */
12382 if (tape.recording)
12383 TapeRecordAction(tape_action);
12385 #if USE_NEW_PLAYER_ASSIGNMENTS
12387 byte mapped_action[MAX_PLAYERS];
12389 for (i = 0; i < MAX_PLAYERS; i++)
12390 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12392 for (i = 0; i < MAX_PLAYERS; i++)
12393 stored_player[i].effective_action = mapped_action[i];
12397 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12399 GameActions_EM_Main();
12401 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12403 GameActions_SP_Main();
12411 void GameActions_EM_Main()
12413 byte effective_action[MAX_PLAYERS];
12414 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12417 for (i = 0; i < MAX_PLAYERS; i++)
12418 effective_action[i] = stored_player[i].effective_action;
12420 GameActions_EM(effective_action, warp_mode);
12424 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12427 void GameActions_SP_Main()
12429 byte effective_action[MAX_PLAYERS];
12430 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12433 for (i = 0; i < MAX_PLAYERS; i++)
12434 effective_action[i] = stored_player[i].effective_action;
12436 GameActions_SP(effective_action, warp_mode);
12440 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12443 void GameActions_RND()
12445 int magic_wall_x = 0, magic_wall_y = 0;
12446 int i, x, y, element, graphic;
12448 InitPlayfieldScanModeVars();
12450 #if USE_ONE_MORE_CHANGE_PER_FRAME
12451 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12453 SCAN_PLAYFIELD(x, y)
12455 ChangeCount[x][y] = 0;
12456 ChangeEvent[x][y] = -1;
12461 if (game.set_centered_player)
12463 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12465 /* switching to "all players" only possible if all players fit to screen */
12466 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12468 game.centered_player_nr_next = game.centered_player_nr;
12469 game.set_centered_player = FALSE;
12472 /* do not switch focus to non-existing (or non-active) player */
12473 if (game.centered_player_nr_next >= 0 &&
12474 !stored_player[game.centered_player_nr_next].active)
12476 game.centered_player_nr_next = game.centered_player_nr;
12477 game.set_centered_player = FALSE;
12481 if (game.set_centered_player &&
12482 ScreenMovPos == 0) /* screen currently aligned at tile position */
12486 if (game.centered_player_nr_next == -1)
12488 setScreenCenteredToAllPlayers(&sx, &sy);
12492 sx = stored_player[game.centered_player_nr_next].jx;
12493 sy = stored_player[game.centered_player_nr_next].jy;
12496 game.centered_player_nr = game.centered_player_nr_next;
12497 game.set_centered_player = FALSE;
12499 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12500 DrawGameDoorValues();
12503 for (i = 0; i < MAX_PLAYERS; i++)
12505 int actual_player_action = stored_player[i].effective_action;
12508 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12509 - rnd_equinox_tetrachloride 048
12510 - rnd_equinox_tetrachloride_ii 096
12511 - rnd_emanuel_schmieg 002
12512 - doctor_sloan_ww 001, 020
12514 if (stored_player[i].MovPos == 0)
12515 CheckGravityMovement(&stored_player[i]);
12518 /* overwrite programmed action with tape action */
12519 if (stored_player[i].programmed_action)
12520 actual_player_action = stored_player[i].programmed_action;
12522 PlayerActions(&stored_player[i], actual_player_action);
12524 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12527 ScrollScreen(NULL, SCROLL_GO_ON);
12529 /* for backwards compatibility, the following code emulates a fixed bug that
12530 occured when pushing elements (causing elements that just made their last
12531 pushing step to already (if possible) make their first falling step in the
12532 same game frame, which is bad); this code is also needed to use the famous
12533 "spring push bug" which is used in older levels and might be wanted to be
12534 used also in newer levels, but in this case the buggy pushing code is only
12535 affecting the "spring" element and no other elements */
12537 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12539 for (i = 0; i < MAX_PLAYERS; i++)
12541 struct PlayerInfo *player = &stored_player[i];
12542 int x = player->jx;
12543 int y = player->jy;
12545 if (player->active && player->is_pushing && player->is_moving &&
12547 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12548 Feld[x][y] == EL_SPRING))
12550 ContinueMoving(x, y);
12552 /* continue moving after pushing (this is actually a bug) */
12553 if (!IS_MOVING(x, y))
12554 Stop[x][y] = FALSE;
12560 debug_print_timestamp(0, "start main loop profiling");
12563 SCAN_PLAYFIELD(x, y)
12565 ChangeCount[x][y] = 0;
12566 ChangeEvent[x][y] = -1;
12568 /* this must be handled before main playfield loop */
12569 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12572 if (MovDelay[x][y] <= 0)
12576 #if USE_NEW_SNAP_DELAY
12577 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12580 if (MovDelay[x][y] <= 0)
12583 TEST_DrawLevelField(x, y);
12585 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12591 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12593 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12594 printf("GameActions(): This should never happen!\n");
12596 ChangePage[x][y] = -1;
12600 Stop[x][y] = FALSE;
12601 if (WasJustMoving[x][y] > 0)
12602 WasJustMoving[x][y]--;
12603 if (WasJustFalling[x][y] > 0)
12604 WasJustFalling[x][y]--;
12605 if (CheckCollision[x][y] > 0)
12606 CheckCollision[x][y]--;
12607 if (CheckImpact[x][y] > 0)
12608 CheckImpact[x][y]--;
12612 /* reset finished pushing action (not done in ContinueMoving() to allow
12613 continuous pushing animation for elements with zero push delay) */
12614 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12616 ResetGfxAnimation(x, y);
12617 TEST_DrawLevelField(x, y);
12621 if (IS_BLOCKED(x, y))
12625 Blocked2Moving(x, y, &oldx, &oldy);
12626 if (!IS_MOVING(oldx, oldy))
12628 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12629 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12630 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12631 printf("GameActions(): This should never happen!\n");
12638 debug_print_timestamp(0, "- time for pre-main loop:");
12641 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12642 SCAN_PLAYFIELD(x, y)
12644 element = Feld[x][y];
12645 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12650 int element2 = element;
12651 int graphic2 = graphic;
12653 int element2 = Feld[x][y];
12654 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12656 int last_gfx_frame = GfxFrame[x][y];
12658 if (graphic_info[graphic2].anim_global_sync)
12659 GfxFrame[x][y] = FrameCounter;
12660 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12661 GfxFrame[x][y] = CustomValue[x][y];
12662 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12663 GfxFrame[x][y] = element_info[element2].collect_score;
12664 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12665 GfxFrame[x][y] = ChangeDelay[x][y];
12667 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12668 DrawLevelGraphicAnimation(x, y, graphic2);
12671 ResetGfxFrame(x, y, TRUE);
12675 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12676 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12677 ResetRandomAnimationValue(x, y);
12681 SetRandomAnimationValue(x, y);
12685 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12688 #endif // -------------------- !!! TEST ONLY !!! --------------------
12691 debug_print_timestamp(0, "- time for TEST loop: -->");
12694 SCAN_PLAYFIELD(x, y)
12696 element = Feld[x][y];
12697 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12699 ResetGfxFrame(x, y, TRUE);
12701 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12702 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12703 ResetRandomAnimationValue(x, y);
12705 SetRandomAnimationValue(x, y);
12707 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12709 if (IS_INACTIVE(element))
12711 if (IS_ANIMATED(graphic))
12712 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12717 /* this may take place after moving, so 'element' may have changed */
12718 if (IS_CHANGING(x, y) &&
12719 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12721 int page = element_info[element].event_page_nr[CE_DELAY];
12724 HandleElementChange(x, y, page);
12726 if (CAN_CHANGE(element))
12727 HandleElementChange(x, y, page);
12729 if (HAS_ACTION(element))
12730 ExecuteCustomElementAction(x, y, element, page);
12733 element = Feld[x][y];
12734 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12737 #if 0 // ---------------------------------------------------------------------
12739 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12743 element = Feld[x][y];
12744 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12746 if (IS_ANIMATED(graphic) &&
12747 !IS_MOVING(x, y) &&
12749 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12751 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12752 TEST_DrawTwinkleOnField(x, y);
12754 else if (IS_MOVING(x, y))
12755 ContinueMoving(x, y);
12762 case EL_EM_EXIT_OPEN:
12763 case EL_SP_EXIT_OPEN:
12764 case EL_STEEL_EXIT_OPEN:
12765 case EL_EM_STEEL_EXIT_OPEN:
12766 case EL_SP_TERMINAL:
12767 case EL_SP_TERMINAL_ACTIVE:
12768 case EL_EXTRA_TIME:
12769 case EL_SHIELD_NORMAL:
12770 case EL_SHIELD_DEADLY:
12771 if (IS_ANIMATED(graphic))
12772 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12775 case EL_DYNAMITE_ACTIVE:
12776 case EL_EM_DYNAMITE_ACTIVE:
12777 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12778 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12779 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12780 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12781 case EL_SP_DISK_RED_ACTIVE:
12782 CheckDynamite(x, y);
12785 case EL_AMOEBA_GROWING:
12786 AmoebeWaechst(x, y);
12789 case EL_AMOEBA_SHRINKING:
12790 AmoebaDisappearing(x, y);
12793 #if !USE_NEW_AMOEBA_CODE
12794 case EL_AMOEBA_WET:
12795 case EL_AMOEBA_DRY:
12796 case EL_AMOEBA_FULL:
12798 case EL_EMC_DRIPPER:
12799 AmoebeAbleger(x, y);
12803 case EL_GAME_OF_LIFE:
12808 case EL_EXIT_CLOSED:
12812 case EL_EM_EXIT_CLOSED:
12816 case EL_STEEL_EXIT_CLOSED:
12817 CheckExitSteel(x, y);
12820 case EL_EM_STEEL_EXIT_CLOSED:
12821 CheckExitSteelEM(x, y);
12824 case EL_SP_EXIT_CLOSED:
12828 case EL_EXPANDABLE_WALL_GROWING:
12829 case EL_EXPANDABLE_STEELWALL_GROWING:
12830 MauerWaechst(x, y);
12833 case EL_EXPANDABLE_WALL:
12834 case EL_EXPANDABLE_WALL_HORIZONTAL:
12835 case EL_EXPANDABLE_WALL_VERTICAL:
12836 case EL_EXPANDABLE_WALL_ANY:
12837 case EL_BD_EXPANDABLE_WALL:
12838 MauerAbleger(x, y);
12841 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12842 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12843 case EL_EXPANDABLE_STEELWALL_ANY:
12844 MauerAblegerStahl(x, y);
12848 CheckForDragon(x, y);
12854 case EL_ELEMENT_SNAPPING:
12855 case EL_DIAGONAL_SHRINKING:
12856 case EL_DIAGONAL_GROWING:
12859 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12861 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12866 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12867 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12872 #else // ---------------------------------------------------------------------
12874 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12878 element = Feld[x][y];
12879 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12881 if (IS_ANIMATED(graphic) &&
12882 !IS_MOVING(x, y) &&
12884 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12886 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12887 TEST_DrawTwinkleOnField(x, y);
12889 else if ((element == EL_ACID ||
12890 element == EL_EXIT_OPEN ||
12891 element == EL_EM_EXIT_OPEN ||
12892 element == EL_SP_EXIT_OPEN ||
12893 element == EL_STEEL_EXIT_OPEN ||
12894 element == EL_EM_STEEL_EXIT_OPEN ||
12895 element == EL_SP_TERMINAL ||
12896 element == EL_SP_TERMINAL_ACTIVE ||
12897 element == EL_EXTRA_TIME ||
12898 element == EL_SHIELD_NORMAL ||
12899 element == EL_SHIELD_DEADLY) &&
12900 IS_ANIMATED(graphic))
12901 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12902 else if (IS_MOVING(x, y))
12903 ContinueMoving(x, y);
12904 else if (IS_ACTIVE_BOMB(element))
12905 CheckDynamite(x, y);
12906 else if (element == EL_AMOEBA_GROWING)
12907 AmoebeWaechst(x, y);
12908 else if (element == EL_AMOEBA_SHRINKING)
12909 AmoebaDisappearing(x, y);
12911 #if !USE_NEW_AMOEBA_CODE
12912 else if (IS_AMOEBALIVE(element))
12913 AmoebeAbleger(x, y);
12916 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12918 else if (element == EL_EXIT_CLOSED)
12920 else if (element == EL_EM_EXIT_CLOSED)
12922 else if (element == EL_STEEL_EXIT_CLOSED)
12923 CheckExitSteel(x, y);
12924 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12925 CheckExitSteelEM(x, y);
12926 else if (element == EL_SP_EXIT_CLOSED)
12928 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12929 element == EL_EXPANDABLE_STEELWALL_GROWING)
12930 MauerWaechst(x, y);
12931 else if (element == EL_EXPANDABLE_WALL ||
12932 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12933 element == EL_EXPANDABLE_WALL_VERTICAL ||
12934 element == EL_EXPANDABLE_WALL_ANY ||
12935 element == EL_BD_EXPANDABLE_WALL)
12936 MauerAbleger(x, y);
12937 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12938 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12939 element == EL_EXPANDABLE_STEELWALL_ANY)
12940 MauerAblegerStahl(x, y);
12941 else if (element == EL_FLAMES)
12942 CheckForDragon(x, y);
12943 else if (element == EL_EXPLOSION)
12944 ; /* drawing of correct explosion animation is handled separately */
12945 else if (element == EL_ELEMENT_SNAPPING ||
12946 element == EL_DIAGONAL_SHRINKING ||
12947 element == EL_DIAGONAL_GROWING)
12949 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12951 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12953 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12954 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12956 #endif // ---------------------------------------------------------------------
12958 if (IS_BELT_ACTIVE(element))
12959 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12961 if (game.magic_wall_active)
12963 int jx = local_player->jx, jy = local_player->jy;
12965 /* play the element sound at the position nearest to the player */
12966 if ((element == EL_MAGIC_WALL_FULL ||
12967 element == EL_MAGIC_WALL_ACTIVE ||
12968 element == EL_MAGIC_WALL_EMPTYING ||
12969 element == EL_BD_MAGIC_WALL_FULL ||
12970 element == EL_BD_MAGIC_WALL_ACTIVE ||
12971 element == EL_BD_MAGIC_WALL_EMPTYING ||
12972 element == EL_DC_MAGIC_WALL_FULL ||
12973 element == EL_DC_MAGIC_WALL_ACTIVE ||
12974 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12975 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12984 debug_print_timestamp(0, "- time for MAIN loop: -->");
12987 #if USE_NEW_AMOEBA_CODE
12988 /* new experimental amoeba growth stuff */
12989 if (!(FrameCounter % 8))
12991 static unsigned long random = 1684108901;
12993 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12995 x = RND(lev_fieldx);
12996 y = RND(lev_fieldy);
12997 element = Feld[x][y];
12999 if (!IS_PLAYER(x,y) &&
13000 (element == EL_EMPTY ||
13001 CAN_GROW_INTO(element) ||
13002 element == EL_QUICKSAND_EMPTY ||
13003 element == EL_QUICKSAND_FAST_EMPTY ||
13004 element == EL_ACID_SPLASH_LEFT ||
13005 element == EL_ACID_SPLASH_RIGHT))
13007 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13008 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13009 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13010 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13011 Feld[x][y] = EL_AMOEBA_DROP;
13014 random = random * 129 + 1;
13020 if (game.explosions_delayed)
13023 game.explosions_delayed = FALSE;
13025 SCAN_PLAYFIELD(x, y)
13027 element = Feld[x][y];
13029 if (ExplodeField[x][y])
13030 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13031 else if (element == EL_EXPLOSION)
13032 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13034 ExplodeField[x][y] = EX_TYPE_NONE;
13037 game.explosions_delayed = TRUE;
13040 if (game.magic_wall_active)
13042 if (!(game.magic_wall_time_left % 4))
13044 int element = Feld[magic_wall_x][magic_wall_y];
13046 if (element == EL_BD_MAGIC_WALL_FULL ||
13047 element == EL_BD_MAGIC_WALL_ACTIVE ||
13048 element == EL_BD_MAGIC_WALL_EMPTYING)
13049 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13050 else if (element == EL_DC_MAGIC_WALL_FULL ||
13051 element == EL_DC_MAGIC_WALL_ACTIVE ||
13052 element == EL_DC_MAGIC_WALL_EMPTYING)
13053 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13055 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13058 if (game.magic_wall_time_left > 0)
13060 game.magic_wall_time_left--;
13062 if (!game.magic_wall_time_left)
13064 SCAN_PLAYFIELD(x, y)
13066 element = Feld[x][y];
13068 if (element == EL_MAGIC_WALL_ACTIVE ||
13069 element == EL_MAGIC_WALL_FULL)
13071 Feld[x][y] = EL_MAGIC_WALL_DEAD;
13072 TEST_DrawLevelField(x, y);
13074 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13075 element == EL_BD_MAGIC_WALL_FULL)
13077 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13078 TEST_DrawLevelField(x, y);
13080 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13081 element == EL_DC_MAGIC_WALL_FULL)
13083 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13084 TEST_DrawLevelField(x, y);
13088 game.magic_wall_active = FALSE;
13093 if (game.light_time_left > 0)
13095 game.light_time_left--;
13097 if (game.light_time_left == 0)
13098 RedrawAllLightSwitchesAndInvisibleElements();
13101 if (game.timegate_time_left > 0)
13103 game.timegate_time_left--;
13105 if (game.timegate_time_left == 0)
13106 CloseAllOpenTimegates();
13109 if (game.lenses_time_left > 0)
13111 game.lenses_time_left--;
13113 if (game.lenses_time_left == 0)
13114 RedrawAllInvisibleElementsForLenses();
13117 if (game.magnify_time_left > 0)
13119 game.magnify_time_left--;
13121 if (game.magnify_time_left == 0)
13122 RedrawAllInvisibleElementsForMagnifier();
13125 for (i = 0; i < MAX_PLAYERS; i++)
13127 struct PlayerInfo *player = &stored_player[i];
13129 if (SHIELD_ON(player))
13131 if (player->shield_deadly_time_left)
13132 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13133 else if (player->shield_normal_time_left)
13134 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13138 #if USE_DELAYED_GFX_REDRAW
13139 SCAN_PLAYFIELD(x, y)
13142 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13144 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13145 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13148 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13149 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13151 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13152 DrawLevelField(x, y);
13154 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13155 DrawLevelFieldCrumbledSand(x, y);
13157 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13158 DrawLevelFieldCrumbledSandNeighbours(x, y);
13160 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13161 DrawTwinkleOnField(x, y);
13164 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13171 PlayAllPlayersSound();
13173 if (options.debug) /* calculate frames per second */
13175 static unsigned long fps_counter = 0;
13176 static int fps_frames = 0;
13177 unsigned long fps_delay_ms = Counter() - fps_counter;
13181 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13183 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13186 fps_counter = Counter();
13189 redraw_mask |= REDRAW_FPS;
13192 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13194 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13196 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13198 local_player->show_envelope = 0;
13202 debug_print_timestamp(0, "stop main loop profiling ");
13203 printf("----------------------------------------------------------\n");
13206 /* use random number generator in every frame to make it less predictable */
13207 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13211 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13213 int min_x = x, min_y = y, max_x = x, max_y = y;
13216 for (i = 0; i < MAX_PLAYERS; i++)
13218 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13220 if (!stored_player[i].active || &stored_player[i] == player)
13223 min_x = MIN(min_x, jx);
13224 min_y = MIN(min_y, jy);
13225 max_x = MAX(max_x, jx);
13226 max_y = MAX(max_y, jy);
13229 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13232 static boolean AllPlayersInVisibleScreen()
13236 for (i = 0; i < MAX_PLAYERS; i++)
13238 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13240 if (!stored_player[i].active)
13243 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13250 void ScrollLevel(int dx, int dy)
13253 /* (directly solved in BlitBitmap() now) */
13254 static Bitmap *bitmap_db_field2 = NULL;
13255 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13262 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13263 /* only horizontal XOR vertical scroll direction allowed */
13264 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13269 /* (directly solved in BlitBitmap() now) */
13270 if (bitmap_db_field2 == NULL)
13271 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13273 /* needed when blitting directly to same bitmap -- should not be needed with
13274 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13275 BlitBitmap(drawto_field, bitmap_db_field2,
13276 FX + TILEX * (dx == -1) - softscroll_offset,
13277 FY + TILEY * (dy == -1) - softscroll_offset,
13278 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13279 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13280 FX + TILEX * (dx == 1) - softscroll_offset,
13281 FY + TILEY * (dy == 1) - softscroll_offset);
13282 BlitBitmap(bitmap_db_field2, drawto_field,
13283 FX + TILEX * (dx == 1) - softscroll_offset,
13284 FY + TILEY * (dy == 1) - softscroll_offset,
13285 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13286 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13287 FX + TILEX * (dx == 1) - softscroll_offset,
13288 FY + TILEY * (dy == 1) - softscroll_offset);
13293 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13294 int xsize = (BX2 - BX1 + 1);
13295 int ysize = (BY2 - BY1 + 1);
13296 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13297 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13298 int step = (start < end ? +1 : -1);
13300 for (i = start; i != end; i += step)
13302 BlitBitmap(drawto_field, drawto_field,
13303 FX + TILEX * (dx != 0 ? i + step : 0),
13304 FY + TILEY * (dy != 0 ? i + step : 0),
13305 TILEX * (dx != 0 ? 1 : xsize),
13306 TILEY * (dy != 0 ? 1 : ysize),
13307 FX + TILEX * (dx != 0 ? i : 0),
13308 FY + TILEY * (dy != 0 ? i : 0));
13313 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13315 BlitBitmap(drawto_field, drawto_field,
13316 FX + TILEX * (dx == -1) - softscroll_offset,
13317 FY + TILEY * (dy == -1) - softscroll_offset,
13318 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13319 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13320 FX + TILEX * (dx == 1) - softscroll_offset,
13321 FY + TILEY * (dy == 1) - softscroll_offset);
13327 x = (dx == 1 ? BX1 : BX2);
13328 for (y = BY1; y <= BY2; y++)
13329 DrawScreenField(x, y);
13334 y = (dy == 1 ? BY1 : BY2);
13335 for (x = BX1; x <= BX2; x++)
13336 DrawScreenField(x, y);
13339 redraw_mask |= REDRAW_FIELD;
13342 static boolean canFallDown(struct PlayerInfo *player)
13344 int jx = player->jx, jy = player->jy;
13346 return (IN_LEV_FIELD(jx, jy + 1) &&
13347 (IS_FREE(jx, jy + 1) ||
13348 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13349 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13350 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13353 static boolean canPassField(int x, int y, int move_dir)
13355 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13356 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13357 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13358 int nextx = x + dx;
13359 int nexty = y + dy;
13360 int element = Feld[x][y];
13362 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13363 !CAN_MOVE(element) &&
13364 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13365 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13366 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13369 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13371 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13372 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13373 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13377 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13378 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13379 (IS_DIGGABLE(Feld[newx][newy]) ||
13380 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13381 canPassField(newx, newy, move_dir)));
13384 static void CheckGravityMovement(struct PlayerInfo *player)
13386 #if USE_PLAYER_GRAVITY
13387 if (player->gravity && !player->programmed_action)
13389 if (game.gravity && !player->programmed_action)
13392 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13393 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13394 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13395 int jx = player->jx, jy = player->jy;
13396 boolean player_is_moving_to_valid_field =
13397 (!player_is_snapping &&
13398 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13399 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13400 boolean player_can_fall_down = canFallDown(player);
13402 if (player_can_fall_down &&
13403 !player_is_moving_to_valid_field)
13404 player->programmed_action = MV_DOWN;
13408 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13410 return CheckGravityMovement(player);
13412 #if USE_PLAYER_GRAVITY
13413 if (player->gravity && !player->programmed_action)
13415 if (game.gravity && !player->programmed_action)
13418 int jx = player->jx, jy = player->jy;
13419 boolean field_under_player_is_free =
13420 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13421 boolean player_is_standing_on_valid_field =
13422 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13423 (IS_WALKABLE(Feld[jx][jy]) &&
13424 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13426 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13427 player->programmed_action = MV_DOWN;
13432 MovePlayerOneStep()
13433 -----------------------------------------------------------------------------
13434 dx, dy: direction (non-diagonal) to try to move the player to
13435 real_dx, real_dy: direction as read from input device (can be diagonal)
13438 boolean MovePlayerOneStep(struct PlayerInfo *player,
13439 int dx, int dy, int real_dx, int real_dy)
13441 int jx = player->jx, jy = player->jy;
13442 int new_jx = jx + dx, new_jy = jy + dy;
13443 #if !USE_FIXED_DONT_RUN_INTO
13447 boolean player_can_move = !player->cannot_move;
13449 if (!player->active || (!dx && !dy))
13450 return MP_NO_ACTION;
13452 player->MovDir = (dx < 0 ? MV_LEFT :
13453 dx > 0 ? MV_RIGHT :
13455 dy > 0 ? MV_DOWN : MV_NONE);
13457 if (!IN_LEV_FIELD(new_jx, new_jy))
13458 return MP_NO_ACTION;
13460 if (!player_can_move)
13462 if (player->MovPos == 0)
13464 player->is_moving = FALSE;
13465 player->is_digging = FALSE;
13466 player->is_collecting = FALSE;
13467 player->is_snapping = FALSE;
13468 player->is_pushing = FALSE;
13473 if (!options.network && game.centered_player_nr == -1 &&
13474 !AllPlayersInSight(player, new_jx, new_jy))
13475 return MP_NO_ACTION;
13477 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13478 return MP_NO_ACTION;
13481 #if !USE_FIXED_DONT_RUN_INTO
13482 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13484 /* (moved to DigField()) */
13485 if (player_can_move && DONT_RUN_INTO(element))
13487 if (element == EL_ACID && dx == 0 && dy == 1)
13489 SplashAcid(new_jx, new_jy);
13490 Feld[jx][jy] = EL_PLAYER_1;
13491 InitMovingField(jx, jy, MV_DOWN);
13492 Store[jx][jy] = EL_ACID;
13493 ContinueMoving(jx, jy);
13494 BuryPlayer(player);
13497 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13503 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13504 if (can_move != MP_MOVING)
13507 /* check if DigField() has caused relocation of the player */
13508 if (player->jx != jx || player->jy != jy)
13509 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13511 StorePlayer[jx][jy] = 0;
13512 player->last_jx = jx;
13513 player->last_jy = jy;
13514 player->jx = new_jx;
13515 player->jy = new_jy;
13516 StorePlayer[new_jx][new_jy] = player->element_nr;
13518 if (player->move_delay_value_next != -1)
13520 player->move_delay_value = player->move_delay_value_next;
13521 player->move_delay_value_next = -1;
13525 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13527 player->step_counter++;
13529 PlayerVisit[jx][jy] = FrameCounter;
13531 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13532 player->is_moving = TRUE;
13536 /* should better be called in MovePlayer(), but this breaks some tapes */
13537 ScrollPlayer(player, SCROLL_INIT);
13543 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13545 int jx = player->jx, jy = player->jy;
13546 int old_jx = jx, old_jy = jy;
13547 int moved = MP_NO_ACTION;
13549 if (!player->active)
13554 if (player->MovPos == 0)
13556 player->is_moving = FALSE;
13557 player->is_digging = FALSE;
13558 player->is_collecting = FALSE;
13559 player->is_snapping = FALSE;
13560 player->is_pushing = FALSE;
13566 if (player->move_delay > 0)
13569 player->move_delay = -1; /* set to "uninitialized" value */
13571 /* store if player is automatically moved to next field */
13572 player->is_auto_moving = (player->programmed_action != MV_NONE);
13574 /* remove the last programmed player action */
13575 player->programmed_action = 0;
13577 if (player->MovPos)
13579 /* should only happen if pre-1.2 tape recordings are played */
13580 /* this is only for backward compatibility */
13582 int original_move_delay_value = player->move_delay_value;
13585 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13589 /* scroll remaining steps with finest movement resolution */
13590 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13592 while (player->MovPos)
13594 ScrollPlayer(player, SCROLL_GO_ON);
13595 ScrollScreen(NULL, SCROLL_GO_ON);
13597 AdvanceFrameAndPlayerCounters(player->index_nr);
13603 player->move_delay_value = original_move_delay_value;
13606 player->is_active = FALSE;
13608 if (player->last_move_dir & MV_HORIZONTAL)
13610 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13611 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13615 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13616 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13619 #if USE_FIXED_BORDER_RUNNING_GFX
13620 if (!moved && !player->is_active)
13622 player->is_moving = FALSE;
13623 player->is_digging = FALSE;
13624 player->is_collecting = FALSE;
13625 player->is_snapping = FALSE;
13626 player->is_pushing = FALSE;
13634 if (moved & MP_MOVING && !ScreenMovPos &&
13635 (player->index_nr == game.centered_player_nr ||
13636 game.centered_player_nr == -1))
13638 if (moved & MP_MOVING && !ScreenMovPos &&
13639 (player == local_player || !options.network))
13642 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13643 int offset = game.scroll_delay_value;
13645 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13647 /* actual player has left the screen -- scroll in that direction */
13648 if (jx != old_jx) /* player has moved horizontally */
13649 scroll_x += (jx - old_jx);
13650 else /* player has moved vertically */
13651 scroll_y += (jy - old_jy);
13655 if (jx != old_jx) /* player has moved horizontally */
13657 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13658 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13659 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13661 /* don't scroll over playfield boundaries */
13662 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13663 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13665 /* don't scroll more than one field at a time */
13666 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13668 /* don't scroll against the player's moving direction */
13669 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13670 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13671 scroll_x = old_scroll_x;
13673 else /* player has moved vertically */
13675 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13676 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13677 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13679 /* don't scroll over playfield boundaries */
13680 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13681 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13683 /* don't scroll more than one field at a time */
13684 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13686 /* don't scroll against the player's moving direction */
13687 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13688 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13689 scroll_y = old_scroll_y;
13693 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13696 if (!options.network && game.centered_player_nr == -1 &&
13697 !AllPlayersInVisibleScreen())
13699 scroll_x = old_scroll_x;
13700 scroll_y = old_scroll_y;
13704 if (!options.network && !AllPlayersInVisibleScreen())
13706 scroll_x = old_scroll_x;
13707 scroll_y = old_scroll_y;
13712 ScrollScreen(player, SCROLL_INIT);
13713 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13718 player->StepFrame = 0;
13720 if (moved & MP_MOVING)
13722 if (old_jx != jx && old_jy == jy)
13723 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13724 else if (old_jx == jx && old_jy != jy)
13725 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13727 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13729 player->last_move_dir = player->MovDir;
13730 player->is_moving = TRUE;
13731 player->is_snapping = FALSE;
13732 player->is_switching = FALSE;
13733 player->is_dropping = FALSE;
13734 player->is_dropping_pressed = FALSE;
13735 player->drop_pressed_delay = 0;
13738 /* should better be called here than above, but this breaks some tapes */
13739 ScrollPlayer(player, SCROLL_INIT);
13744 CheckGravityMovementWhenNotMoving(player);
13746 player->is_moving = FALSE;
13748 /* at this point, the player is allowed to move, but cannot move right now
13749 (e.g. because of something blocking the way) -- ensure that the player
13750 is also allowed to move in the next frame (in old versions before 3.1.1,
13751 the player was forced to wait again for eight frames before next try) */
13753 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13754 player->move_delay = 0; /* allow direct movement in the next frame */
13757 if (player->move_delay == -1) /* not yet initialized by DigField() */
13758 player->move_delay = player->move_delay_value;
13760 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13762 TestIfPlayerTouchesBadThing(jx, jy);
13763 TestIfPlayerTouchesCustomElement(jx, jy);
13766 if (!player->active)
13767 RemovePlayer(player);
13772 void ScrollPlayer(struct PlayerInfo *player, int mode)
13774 int jx = player->jx, jy = player->jy;
13775 int last_jx = player->last_jx, last_jy = player->last_jy;
13776 int move_stepsize = TILEX / player->move_delay_value;
13778 #if USE_NEW_PLAYER_SPEED
13779 if (!player->active)
13782 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13785 if (!player->active || player->MovPos == 0)
13789 if (mode == SCROLL_INIT)
13791 player->actual_frame_counter = FrameCounter;
13792 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13794 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13795 Feld[last_jx][last_jy] == EL_EMPTY)
13797 int last_field_block_delay = 0; /* start with no blocking at all */
13798 int block_delay_adjustment = player->block_delay_adjustment;
13800 /* if player blocks last field, add delay for exactly one move */
13801 if (player->block_last_field)
13803 last_field_block_delay += player->move_delay_value;
13805 /* when blocking enabled, prevent moving up despite gravity */
13806 #if USE_PLAYER_GRAVITY
13807 if (player->gravity && player->MovDir == MV_UP)
13808 block_delay_adjustment = -1;
13810 if (game.gravity && player->MovDir == MV_UP)
13811 block_delay_adjustment = -1;
13815 /* add block delay adjustment (also possible when not blocking) */
13816 last_field_block_delay += block_delay_adjustment;
13818 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13819 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13822 #if USE_NEW_PLAYER_SPEED
13823 if (player->MovPos != 0) /* player has not yet reached destination */
13829 else if (!FrameReached(&player->actual_frame_counter, 1))
13832 #if USE_NEW_PLAYER_SPEED
13833 if (player->MovPos != 0)
13835 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13836 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13838 /* before DrawPlayer() to draw correct player graphic for this case */
13839 if (player->MovPos == 0)
13840 CheckGravityMovement(player);
13843 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13844 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13846 /* before DrawPlayer() to draw correct player graphic for this case */
13847 if (player->MovPos == 0)
13848 CheckGravityMovement(player);
13851 if (player->MovPos == 0) /* player reached destination field */
13853 if (player->move_delay_reset_counter > 0)
13855 player->move_delay_reset_counter--;
13857 if (player->move_delay_reset_counter == 0)
13859 /* continue with normal speed after quickly moving through gate */
13860 HALVE_PLAYER_SPEED(player);
13862 /* be able to make the next move without delay */
13863 player->move_delay = 0;
13867 player->last_jx = jx;
13868 player->last_jy = jy;
13870 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13871 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13873 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13875 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13876 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13878 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13880 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13881 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13883 DrawPlayer(player); /* needed here only to cleanup last field */
13884 RemovePlayer(player);
13886 if (local_player->friends_still_needed == 0 ||
13887 IS_SP_ELEMENT(Feld[jx][jy]))
13888 PlayerWins(player);
13891 /* this breaks one level: "machine", level 000 */
13893 int move_direction = player->MovDir;
13894 int enter_side = MV_DIR_OPPOSITE(move_direction);
13895 int leave_side = move_direction;
13896 int old_jx = last_jx;
13897 int old_jy = last_jy;
13898 int old_element = Feld[old_jx][old_jy];
13899 int new_element = Feld[jx][jy];
13901 if (IS_CUSTOM_ELEMENT(old_element))
13902 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13904 player->index_bit, leave_side);
13906 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13907 CE_PLAYER_LEAVES_X,
13908 player->index_bit, leave_side);
13910 if (IS_CUSTOM_ELEMENT(new_element))
13911 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13912 player->index_bit, enter_side);
13914 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13915 CE_PLAYER_ENTERS_X,
13916 player->index_bit, enter_side);
13918 #if USE_FIX_CE_ACTION_WITH_PLAYER
13919 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13920 CE_MOVE_OF_X, move_direction);
13922 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13923 CE_MOVE_OF_X, move_direction);
13927 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13929 TestIfPlayerTouchesBadThing(jx, jy);
13930 TestIfPlayerTouchesCustomElement(jx, jy);
13932 /* needed because pushed element has not yet reached its destination,
13933 so it would trigger a change event at its previous field location */
13934 if (!player->is_pushing)
13935 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13937 if (!player->active)
13938 RemovePlayer(player);
13941 if (!local_player->LevelSolved && level.use_step_counter)
13951 if (TimeLeft <= 10 && setup.time_limit)
13952 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13955 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13957 DisplayGameControlValues();
13959 DrawGameValue_Time(TimeLeft);
13962 if (!TimeLeft && setup.time_limit)
13963 for (i = 0; i < MAX_PLAYERS; i++)
13964 KillPlayer(&stored_player[i]);
13967 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13969 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13971 DisplayGameControlValues();
13974 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13975 DrawGameValue_Time(TimePlayed);
13979 if (tape.single_step && tape.recording && !tape.pausing &&
13980 !player->programmed_action)
13981 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13985 void ScrollScreen(struct PlayerInfo *player, int mode)
13987 static unsigned long screen_frame_counter = 0;
13989 if (mode == SCROLL_INIT)
13991 /* set scrolling step size according to actual player's moving speed */
13992 ScrollStepSize = TILEX / player->move_delay_value;
13994 screen_frame_counter = FrameCounter;
13995 ScreenMovDir = player->MovDir;
13996 ScreenMovPos = player->MovPos;
13997 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14000 else if (!FrameReached(&screen_frame_counter, 1))
14005 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14006 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14007 redraw_mask |= REDRAW_FIELD;
14010 ScreenMovDir = MV_NONE;
14013 void TestIfPlayerTouchesCustomElement(int x, int y)
14015 static int xy[4][2] =
14022 static int trigger_sides[4][2] =
14024 /* center side border side */
14025 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14026 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14027 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14028 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14030 static int touch_dir[4] =
14032 MV_LEFT | MV_RIGHT,
14037 int center_element = Feld[x][y]; /* should always be non-moving! */
14040 for (i = 0; i < NUM_DIRECTIONS; i++)
14042 int xx = x + xy[i][0];
14043 int yy = y + xy[i][1];
14044 int center_side = trigger_sides[i][0];
14045 int border_side = trigger_sides[i][1];
14046 int border_element;
14048 if (!IN_LEV_FIELD(xx, yy))
14051 if (IS_PLAYER(x, y)) /* player found at center element */
14053 struct PlayerInfo *player = PLAYERINFO(x, y);
14055 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14056 border_element = Feld[xx][yy]; /* may be moving! */
14057 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14058 border_element = Feld[xx][yy];
14059 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14060 border_element = MovingOrBlocked2Element(xx, yy);
14062 continue; /* center and border element do not touch */
14064 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14065 player->index_bit, border_side);
14066 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14067 CE_PLAYER_TOUCHES_X,
14068 player->index_bit, border_side);
14070 #if USE_FIX_CE_ACTION_WITH_PLAYER
14072 /* use player element that is initially defined in the level playfield,
14073 not the player element that corresponds to the runtime player number
14074 (example: a level that contains EL_PLAYER_3 as the only player would
14075 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14076 int player_element = PLAYERINFO(x, y)->initial_element;
14078 CheckElementChangeBySide(xx, yy, border_element, player_element,
14079 CE_TOUCHING_X, border_side);
14083 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14085 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14087 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14089 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14090 continue; /* center and border element do not touch */
14093 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14094 player->index_bit, center_side);
14095 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14096 CE_PLAYER_TOUCHES_X,
14097 player->index_bit, center_side);
14099 #if USE_FIX_CE_ACTION_WITH_PLAYER
14101 /* use player element that is initially defined in the level playfield,
14102 not the player element that corresponds to the runtime player number
14103 (example: a level that contains EL_PLAYER_3 as the only player would
14104 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14105 int player_element = PLAYERINFO(xx, yy)->initial_element;
14107 CheckElementChangeBySide(x, y, center_element, player_element,
14108 CE_TOUCHING_X, center_side);
14117 #if USE_ELEMENT_TOUCHING_BUGFIX
14119 void TestIfElementTouchesCustomElement(int x, int y)
14121 static int xy[4][2] =
14128 static int trigger_sides[4][2] =
14130 /* center side border side */
14131 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14132 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14133 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14134 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14136 static int touch_dir[4] =
14138 MV_LEFT | MV_RIGHT,
14143 boolean change_center_element = FALSE;
14144 int center_element = Feld[x][y]; /* should always be non-moving! */
14145 int border_element_old[NUM_DIRECTIONS];
14148 for (i = 0; i < NUM_DIRECTIONS; i++)
14150 int xx = x + xy[i][0];
14151 int yy = y + xy[i][1];
14152 int border_element;
14154 border_element_old[i] = -1;
14156 if (!IN_LEV_FIELD(xx, yy))
14159 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14160 border_element = Feld[xx][yy]; /* may be moving! */
14161 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14162 border_element = Feld[xx][yy];
14163 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14164 border_element = MovingOrBlocked2Element(xx, yy);
14166 continue; /* center and border element do not touch */
14168 border_element_old[i] = border_element;
14171 for (i = 0; i < NUM_DIRECTIONS; i++)
14173 int xx = x + xy[i][0];
14174 int yy = y + xy[i][1];
14175 int center_side = trigger_sides[i][0];
14176 int border_element = border_element_old[i];
14178 if (border_element == -1)
14181 /* check for change of border element */
14182 CheckElementChangeBySide(xx, yy, border_element, center_element,
14183 CE_TOUCHING_X, center_side);
14185 /* (center element cannot be player, so we dont have to check this here) */
14188 for (i = 0; i < NUM_DIRECTIONS; i++)
14190 int xx = x + xy[i][0];
14191 int yy = y + xy[i][1];
14192 int border_side = trigger_sides[i][1];
14193 int border_element = border_element_old[i];
14195 if (border_element == -1)
14198 /* check for change of center element (but change it only once) */
14199 if (!change_center_element)
14200 change_center_element =
14201 CheckElementChangeBySide(x, y, center_element, border_element,
14202 CE_TOUCHING_X, border_side);
14204 #if USE_FIX_CE_ACTION_WITH_PLAYER
14205 if (IS_PLAYER(xx, yy))
14207 /* use player element that is initially defined in the level playfield,
14208 not the player element that corresponds to the runtime player number
14209 (example: a level that contains EL_PLAYER_3 as the only player would
14210 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14211 int player_element = PLAYERINFO(xx, yy)->initial_element;
14213 CheckElementChangeBySide(x, y, center_element, player_element,
14214 CE_TOUCHING_X, border_side);
14222 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14224 static int xy[4][2] =
14231 static int trigger_sides[4][2] =
14233 /* center side border side */
14234 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14235 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14236 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14237 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14239 static int touch_dir[4] =
14241 MV_LEFT | MV_RIGHT,
14246 boolean change_center_element = FALSE;
14247 int center_element = Feld[x][y]; /* should always be non-moving! */
14250 for (i = 0; i < NUM_DIRECTIONS; i++)
14252 int xx = x + xy[i][0];
14253 int yy = y + xy[i][1];
14254 int center_side = trigger_sides[i][0];
14255 int border_side = trigger_sides[i][1];
14256 int border_element;
14258 if (!IN_LEV_FIELD(xx, yy))
14261 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14262 border_element = Feld[xx][yy]; /* may be moving! */
14263 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14264 border_element = Feld[xx][yy];
14265 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14266 border_element = MovingOrBlocked2Element(xx, yy);
14268 continue; /* center and border element do not touch */
14270 /* check for change of center element (but change it only once) */
14271 if (!change_center_element)
14272 change_center_element =
14273 CheckElementChangeBySide(x, y, center_element, border_element,
14274 CE_TOUCHING_X, border_side);
14276 /* check for change of border element */
14277 CheckElementChangeBySide(xx, yy, border_element, center_element,
14278 CE_TOUCHING_X, center_side);
14284 void TestIfElementHitsCustomElement(int x, int y, int direction)
14286 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14287 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14288 int hitx = x + dx, hity = y + dy;
14289 int hitting_element = Feld[x][y];
14290 int touched_element;
14292 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14295 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14296 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14298 if (IN_LEV_FIELD(hitx, hity))
14300 int opposite_direction = MV_DIR_OPPOSITE(direction);
14301 int hitting_side = direction;
14302 int touched_side = opposite_direction;
14303 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14304 MovDir[hitx][hity] != direction ||
14305 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14311 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14312 CE_HITTING_X, touched_side);
14314 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14315 CE_HIT_BY_X, hitting_side);
14317 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318 CE_HIT_BY_SOMETHING, opposite_direction);
14320 #if USE_FIX_CE_ACTION_WITH_PLAYER
14321 if (IS_PLAYER(hitx, hity))
14323 /* use player element that is initially defined in the level playfield,
14324 not the player element that corresponds to the runtime player number
14325 (example: a level that contains EL_PLAYER_3 as the only player would
14326 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14327 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14329 CheckElementChangeBySide(x, y, hitting_element, player_element,
14330 CE_HITTING_X, touched_side);
14336 /* "hitting something" is also true when hitting the playfield border */
14337 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14338 CE_HITTING_SOMETHING, direction);
14342 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14344 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14345 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14346 int hitx = x + dx, hity = y + dy;
14347 int hitting_element = Feld[x][y];
14348 int touched_element;
14350 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14351 !IS_FREE(hitx, hity) &&
14352 (!IS_MOVING(hitx, hity) ||
14353 MovDir[hitx][hity] != direction ||
14354 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14357 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14361 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14365 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14366 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14368 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14369 EP_CAN_SMASH_EVERYTHING, direction);
14371 if (IN_LEV_FIELD(hitx, hity))
14373 int opposite_direction = MV_DIR_OPPOSITE(direction);
14374 int hitting_side = direction;
14375 int touched_side = opposite_direction;
14377 int touched_element = MovingOrBlocked2Element(hitx, hity);
14380 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14381 MovDir[hitx][hity] != direction ||
14382 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14391 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14392 CE_SMASHED_BY_SOMETHING, opposite_direction);
14394 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14395 CE_OTHER_IS_SMASHING, touched_side);
14397 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14398 CE_OTHER_GETS_SMASHED, hitting_side);
14404 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14406 int i, kill_x = -1, kill_y = -1;
14408 int bad_element = -1;
14409 static int test_xy[4][2] =
14416 static int test_dir[4] =
14424 for (i = 0; i < NUM_DIRECTIONS; i++)
14426 int test_x, test_y, test_move_dir, test_element;
14428 test_x = good_x + test_xy[i][0];
14429 test_y = good_y + test_xy[i][1];
14431 if (!IN_LEV_FIELD(test_x, test_y))
14435 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14437 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14439 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14440 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14442 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14443 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14447 bad_element = test_element;
14453 if (kill_x != -1 || kill_y != -1)
14455 if (IS_PLAYER(good_x, good_y))
14457 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14459 if (player->shield_deadly_time_left > 0 &&
14460 !IS_INDESTRUCTIBLE(bad_element))
14461 Bang(kill_x, kill_y);
14462 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14463 KillPlayer(player);
14466 Bang(good_x, good_y);
14470 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14472 int i, kill_x = -1, kill_y = -1;
14473 int bad_element = Feld[bad_x][bad_y];
14474 static int test_xy[4][2] =
14481 static int touch_dir[4] =
14483 MV_LEFT | MV_RIGHT,
14488 static int test_dir[4] =
14496 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14499 for (i = 0; i < NUM_DIRECTIONS; i++)
14501 int test_x, test_y, test_move_dir, test_element;
14503 test_x = bad_x + test_xy[i][0];
14504 test_y = bad_y + test_xy[i][1];
14506 if (!IN_LEV_FIELD(test_x, test_y))
14510 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14512 test_element = Feld[test_x][test_y];
14514 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14515 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14517 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14518 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14520 /* good thing is player or penguin that does not move away */
14521 if (IS_PLAYER(test_x, test_y))
14523 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14525 if (bad_element == EL_ROBOT && player->is_moving)
14526 continue; /* robot does not kill player if he is moving */
14528 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14530 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14531 continue; /* center and border element do not touch */
14539 else if (test_element == EL_PENGUIN)
14549 if (kill_x != -1 || kill_y != -1)
14551 if (IS_PLAYER(kill_x, kill_y))
14553 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14555 if (player->shield_deadly_time_left > 0 &&
14556 !IS_INDESTRUCTIBLE(bad_element))
14557 Bang(bad_x, bad_y);
14558 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14559 KillPlayer(player);
14562 Bang(kill_x, kill_y);
14566 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14568 int bad_element = Feld[bad_x][bad_y];
14569 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14570 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14571 int test_x = bad_x + dx, test_y = bad_y + dy;
14572 int test_move_dir, test_element;
14573 int kill_x = -1, kill_y = -1;
14575 if (!IN_LEV_FIELD(test_x, test_y))
14579 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14581 test_element = Feld[test_x][test_y];
14583 if (test_move_dir != bad_move_dir)
14585 /* good thing can be player or penguin that does not move away */
14586 if (IS_PLAYER(test_x, test_y))
14588 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14590 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14591 player as being hit when he is moving towards the bad thing, because
14592 the "get hit by" condition would be lost after the player stops) */
14593 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14594 return; /* player moves away from bad thing */
14599 else if (test_element == EL_PENGUIN)
14606 if (kill_x != -1 || kill_y != -1)
14608 if (IS_PLAYER(kill_x, kill_y))
14610 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14612 if (player->shield_deadly_time_left > 0 &&
14613 !IS_INDESTRUCTIBLE(bad_element))
14614 Bang(bad_x, bad_y);
14615 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14616 KillPlayer(player);
14619 Bang(kill_x, kill_y);
14623 void TestIfPlayerTouchesBadThing(int x, int y)
14625 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14628 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14630 TestIfGoodThingHitsBadThing(x, y, move_dir);
14633 void TestIfBadThingTouchesPlayer(int x, int y)
14635 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14638 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14640 TestIfBadThingHitsGoodThing(x, y, move_dir);
14643 void TestIfFriendTouchesBadThing(int x, int y)
14645 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14648 void TestIfBadThingTouchesFriend(int x, int y)
14650 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14653 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14655 int i, kill_x = bad_x, kill_y = bad_y;
14656 static int xy[4][2] =
14664 for (i = 0; i < NUM_DIRECTIONS; i++)
14668 x = bad_x + xy[i][0];
14669 y = bad_y + xy[i][1];
14670 if (!IN_LEV_FIELD(x, y))
14673 element = Feld[x][y];
14674 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14675 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14683 if (kill_x != bad_x || kill_y != bad_y)
14684 Bang(bad_x, bad_y);
14687 void KillPlayer(struct PlayerInfo *player)
14689 int jx = player->jx, jy = player->jy;
14691 if (!player->active)
14695 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14696 player->killed, player->active, player->reanimated);
14699 /* the following code was introduced to prevent an infinite loop when calling
14701 -> CheckTriggeredElementChangeExt()
14702 -> ExecuteCustomElementAction()
14704 -> (infinitely repeating the above sequence of function calls)
14705 which occurs when killing the player while having a CE with the setting
14706 "kill player X when explosion of <player X>"; the solution using a new
14707 field "player->killed" was chosen for backwards compatibility, although
14708 clever use of the fields "player->active" etc. would probably also work */
14710 if (player->killed)
14714 player->killed = TRUE;
14716 /* remove accessible field at the player's position */
14717 Feld[jx][jy] = EL_EMPTY;
14719 /* deactivate shield (else Bang()/Explode() would not work right) */
14720 player->shield_normal_time_left = 0;
14721 player->shield_deadly_time_left = 0;
14724 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14725 player->killed, player->active, player->reanimated);
14731 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14732 player->killed, player->active, player->reanimated);
14735 #if USE_PLAYER_REANIMATION
14737 if (player->reanimated) /* killed player may have been reanimated */
14738 player->killed = player->reanimated = FALSE;
14740 BuryPlayer(player);
14742 if (player->killed) /* player may have been reanimated */
14743 BuryPlayer(player);
14746 BuryPlayer(player);
14750 static void KillPlayerUnlessEnemyProtected(int x, int y)
14752 if (!PLAYER_ENEMY_PROTECTED(x, y))
14753 KillPlayer(PLAYERINFO(x, y));
14756 static void KillPlayerUnlessExplosionProtected(int x, int y)
14758 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14759 KillPlayer(PLAYERINFO(x, y));
14762 void BuryPlayer(struct PlayerInfo *player)
14764 int jx = player->jx, jy = player->jy;
14766 if (!player->active)
14769 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14770 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14772 player->GameOver = TRUE;
14773 RemovePlayer(player);
14776 void RemovePlayer(struct PlayerInfo *player)
14778 int jx = player->jx, jy = player->jy;
14779 int i, found = FALSE;
14781 player->present = FALSE;
14782 player->active = FALSE;
14784 if (!ExplodeField[jx][jy])
14785 StorePlayer[jx][jy] = 0;
14787 if (player->is_moving)
14788 TEST_DrawLevelField(player->last_jx, player->last_jy);
14790 for (i = 0; i < MAX_PLAYERS; i++)
14791 if (stored_player[i].active)
14795 AllPlayersGone = TRUE;
14801 #if USE_NEW_SNAP_DELAY
14802 static void setFieldForSnapping(int x, int y, int element, int direction)
14804 struct ElementInfo *ei = &element_info[element];
14805 int direction_bit = MV_DIR_TO_BIT(direction);
14806 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14807 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14808 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14810 Feld[x][y] = EL_ELEMENT_SNAPPING;
14811 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14813 ResetGfxAnimation(x, y);
14815 GfxElement[x][y] = element;
14816 GfxAction[x][y] = action;
14817 GfxDir[x][y] = direction;
14818 GfxFrame[x][y] = -1;
14823 =============================================================================
14824 checkDiagonalPushing()
14825 -----------------------------------------------------------------------------
14826 check if diagonal input device direction results in pushing of object
14827 (by checking if the alternative direction is walkable, diggable, ...)
14828 =============================================================================
14831 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14832 int x, int y, int real_dx, int real_dy)
14834 int jx, jy, dx, dy, xx, yy;
14836 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14839 /* diagonal direction: check alternative direction */
14844 xx = jx + (dx == 0 ? real_dx : 0);
14845 yy = jy + (dy == 0 ? real_dy : 0);
14847 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14851 =============================================================================
14853 -----------------------------------------------------------------------------
14854 x, y: field next to player (non-diagonal) to try to dig to
14855 real_dx, real_dy: direction as read from input device (can be diagonal)
14856 =============================================================================
14859 static int DigField(struct PlayerInfo *player,
14860 int oldx, int oldy, int x, int y,
14861 int real_dx, int real_dy, int mode)
14863 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14864 boolean player_was_pushing = player->is_pushing;
14865 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14866 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14867 int jx = oldx, jy = oldy;
14868 int dx = x - jx, dy = y - jy;
14869 int nextx = x + dx, nexty = y + dy;
14870 int move_direction = (dx == -1 ? MV_LEFT :
14871 dx == +1 ? MV_RIGHT :
14873 dy == +1 ? MV_DOWN : MV_NONE);
14874 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14875 int dig_side = MV_DIR_OPPOSITE(move_direction);
14876 int old_element = Feld[jx][jy];
14877 #if USE_FIXED_DONT_RUN_INTO
14878 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14884 if (is_player) /* function can also be called by EL_PENGUIN */
14886 if (player->MovPos == 0)
14888 player->is_digging = FALSE;
14889 player->is_collecting = FALSE;
14892 if (player->MovPos == 0) /* last pushing move finished */
14893 player->is_pushing = FALSE;
14895 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14897 player->is_switching = FALSE;
14898 player->push_delay = -1;
14900 return MP_NO_ACTION;
14904 #if !USE_FIXED_DONT_RUN_INTO
14905 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14906 return MP_NO_ACTION;
14909 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14910 old_element = Back[jx][jy];
14912 /* in case of element dropped at player position, check background */
14913 else if (Back[jx][jy] != EL_EMPTY &&
14914 game.engine_version >= VERSION_IDENT(2,2,0,0))
14915 old_element = Back[jx][jy];
14917 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14918 return MP_NO_ACTION; /* field has no opening in this direction */
14920 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14921 return MP_NO_ACTION; /* field has no opening in this direction */
14923 #if USE_FIXED_DONT_RUN_INTO
14924 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14928 Feld[jx][jy] = player->artwork_element;
14929 InitMovingField(jx, jy, MV_DOWN);
14930 Store[jx][jy] = EL_ACID;
14931 ContinueMoving(jx, jy);
14932 BuryPlayer(player);
14934 return MP_DONT_RUN_INTO;
14938 #if USE_FIXED_DONT_RUN_INTO
14939 if (player_can_move && DONT_RUN_INTO(element))
14941 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14943 return MP_DONT_RUN_INTO;
14947 #if USE_FIXED_DONT_RUN_INTO
14948 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14949 return MP_NO_ACTION;
14952 #if !USE_FIXED_DONT_RUN_INTO
14953 element = Feld[x][y];
14956 collect_count = element_info[element].collect_count_initial;
14958 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14959 return MP_NO_ACTION;
14961 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14962 player_can_move = player_can_move_or_snap;
14964 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14965 game.engine_version >= VERSION_IDENT(2,2,0,0))
14967 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14968 player->index_bit, dig_side);
14969 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14970 player->index_bit, dig_side);
14972 if (element == EL_DC_LANDMINE)
14975 if (Feld[x][y] != element) /* field changed by snapping */
14978 return MP_NO_ACTION;
14981 #if USE_PLAYER_GRAVITY
14982 if (player->gravity && is_player && !player->is_auto_moving &&
14983 canFallDown(player) && move_direction != MV_DOWN &&
14984 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14985 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14987 if (game.gravity && is_player && !player->is_auto_moving &&
14988 canFallDown(player) && move_direction != MV_DOWN &&
14989 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14990 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14993 if (player_can_move &&
14994 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14996 int sound_element = SND_ELEMENT(element);
14997 int sound_action = ACTION_WALKING;
14999 if (IS_RND_GATE(element))
15001 if (!player->key[RND_GATE_NR(element)])
15002 return MP_NO_ACTION;
15004 else if (IS_RND_GATE_GRAY(element))
15006 if (!player->key[RND_GATE_GRAY_NR(element)])
15007 return MP_NO_ACTION;
15009 else if (IS_RND_GATE_GRAY_ACTIVE(element))
15011 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15012 return MP_NO_ACTION;
15014 else if (element == EL_EXIT_OPEN ||
15015 element == EL_EM_EXIT_OPEN ||
15017 element == EL_EM_EXIT_OPENING ||
15019 element == EL_STEEL_EXIT_OPEN ||
15020 element == EL_EM_STEEL_EXIT_OPEN ||
15022 element == EL_EM_STEEL_EXIT_OPENING ||
15024 element == EL_SP_EXIT_OPEN ||
15025 element == EL_SP_EXIT_OPENING)
15027 sound_action = ACTION_PASSING; /* player is passing exit */
15029 else if (element == EL_EMPTY)
15031 sound_action = ACTION_MOVING; /* nothing to walk on */
15034 /* play sound from background or player, whatever is available */
15035 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15036 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15038 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15040 else if (player_can_move &&
15041 IS_PASSABLE(element) && canPassField(x, y, move_direction))
15043 if (!ACCESS_FROM(element, opposite_direction))
15044 return MP_NO_ACTION; /* field not accessible from this direction */
15046 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
15047 return MP_NO_ACTION;
15049 if (IS_EM_GATE(element))
15051 if (!player->key[EM_GATE_NR(element)])
15052 return MP_NO_ACTION;
15054 else if (IS_EM_GATE_GRAY(element))
15056 if (!player->key[EM_GATE_GRAY_NR(element)])
15057 return MP_NO_ACTION;
15059 else if (IS_EM_GATE_GRAY_ACTIVE(element))
15061 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15062 return MP_NO_ACTION;
15064 else if (IS_EMC_GATE(element))
15066 if (!player->key[EMC_GATE_NR(element)])
15067 return MP_NO_ACTION;
15069 else if (IS_EMC_GATE_GRAY(element))
15071 if (!player->key[EMC_GATE_GRAY_NR(element)])
15072 return MP_NO_ACTION;
15074 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15076 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15077 return MP_NO_ACTION;
15079 else if (element == EL_DC_GATE_WHITE ||
15080 element == EL_DC_GATE_WHITE_GRAY ||
15081 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15083 if (player->num_white_keys == 0)
15084 return MP_NO_ACTION;
15086 player->num_white_keys--;
15088 else if (IS_SP_PORT(element))
15090 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15091 element == EL_SP_GRAVITY_PORT_RIGHT ||
15092 element == EL_SP_GRAVITY_PORT_UP ||
15093 element == EL_SP_GRAVITY_PORT_DOWN)
15094 #if USE_PLAYER_GRAVITY
15095 player->gravity = !player->gravity;
15097 game.gravity = !game.gravity;
15099 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15100 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15101 element == EL_SP_GRAVITY_ON_PORT_UP ||
15102 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15103 #if USE_PLAYER_GRAVITY
15104 player->gravity = TRUE;
15106 game.gravity = TRUE;
15108 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15109 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15110 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15111 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15112 #if USE_PLAYER_GRAVITY
15113 player->gravity = FALSE;
15115 game.gravity = FALSE;
15119 /* automatically move to the next field with double speed */
15120 player->programmed_action = move_direction;
15122 if (player->move_delay_reset_counter == 0)
15124 player->move_delay_reset_counter = 2; /* two double speed steps */
15126 DOUBLE_PLAYER_SPEED(player);
15129 PlayLevelSoundAction(x, y, ACTION_PASSING);
15131 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15135 if (mode != DF_SNAP)
15137 GfxElement[x][y] = GFX_ELEMENT(element);
15138 player->is_digging = TRUE;
15141 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15143 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15144 player->index_bit, dig_side);
15146 if (mode == DF_SNAP)
15148 #if USE_NEW_SNAP_DELAY
15149 if (level.block_snap_field)
15150 setFieldForSnapping(x, y, element, move_direction);
15152 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15154 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15157 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15158 player->index_bit, dig_side);
15161 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15165 if (is_player && mode != DF_SNAP)
15167 GfxElement[x][y] = element;
15168 player->is_collecting = TRUE;
15171 if (element == EL_SPEED_PILL)
15173 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15175 else if (element == EL_EXTRA_TIME && level.time > 0)
15177 TimeLeft += level.extra_time;
15180 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15182 DisplayGameControlValues();
15184 DrawGameValue_Time(TimeLeft);
15187 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15189 player->shield_normal_time_left += level.shield_normal_time;
15190 if (element == EL_SHIELD_DEADLY)
15191 player->shield_deadly_time_left += level.shield_deadly_time;
15193 else if (element == EL_DYNAMITE ||
15194 element == EL_EM_DYNAMITE ||
15195 element == EL_SP_DISK_RED)
15197 if (player->inventory_size < MAX_INVENTORY_SIZE)
15198 player->inventory_element[player->inventory_size++] = element;
15200 DrawGameDoorValues();
15202 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15204 player->dynabomb_count++;
15205 player->dynabombs_left++;
15207 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15209 player->dynabomb_size++;
15211 else if (element == EL_DYNABOMB_INCREASE_POWER)
15213 player->dynabomb_xl = TRUE;
15215 else if (IS_KEY(element))
15217 player->key[KEY_NR(element)] = TRUE;
15219 DrawGameDoorValues();
15221 else if (element == EL_DC_KEY_WHITE)
15223 player->num_white_keys++;
15225 /* display white keys? */
15226 /* DrawGameDoorValues(); */
15228 else if (IS_ENVELOPE(element))
15230 player->show_envelope = element;
15232 else if (element == EL_EMC_LENSES)
15234 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15236 RedrawAllInvisibleElementsForLenses();
15238 else if (element == EL_EMC_MAGNIFIER)
15240 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15242 RedrawAllInvisibleElementsForMagnifier();
15244 else if (IS_DROPPABLE(element) ||
15245 IS_THROWABLE(element)) /* can be collected and dropped */
15249 if (collect_count == 0)
15250 player->inventory_infinite_element = element;
15252 for (i = 0; i < collect_count; i++)
15253 if (player->inventory_size < MAX_INVENTORY_SIZE)
15254 player->inventory_element[player->inventory_size++] = element;
15256 DrawGameDoorValues();
15258 else if (collect_count > 0)
15260 local_player->gems_still_needed -= collect_count;
15261 if (local_player->gems_still_needed < 0)
15262 local_player->gems_still_needed = 0;
15265 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15267 DisplayGameControlValues();
15269 DrawGameValue_Emeralds(local_player->gems_still_needed);
15273 RaiseScoreElement(element);
15274 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15277 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15278 player->index_bit, dig_side);
15280 if (mode == DF_SNAP)
15282 #if USE_NEW_SNAP_DELAY
15283 if (level.block_snap_field)
15284 setFieldForSnapping(x, y, element, move_direction);
15286 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15288 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15291 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15292 player->index_bit, dig_side);
15295 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15297 if (mode == DF_SNAP && element != EL_BD_ROCK)
15298 return MP_NO_ACTION;
15300 if (CAN_FALL(element) && dy)
15301 return MP_NO_ACTION;
15303 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15304 !(element == EL_SPRING && level.use_spring_bug))
15305 return MP_NO_ACTION;
15307 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15308 ((move_direction & MV_VERTICAL &&
15309 ((element_info[element].move_pattern & MV_LEFT &&
15310 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15311 (element_info[element].move_pattern & MV_RIGHT &&
15312 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15313 (move_direction & MV_HORIZONTAL &&
15314 ((element_info[element].move_pattern & MV_UP &&
15315 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15316 (element_info[element].move_pattern & MV_DOWN &&
15317 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15318 return MP_NO_ACTION;
15320 /* do not push elements already moving away faster than player */
15321 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15322 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15323 return MP_NO_ACTION;
15325 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15327 if (player->push_delay_value == -1 || !player_was_pushing)
15328 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15330 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15332 if (player->push_delay_value == -1)
15333 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15335 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15337 if (!player->is_pushing)
15338 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15341 player->is_pushing = TRUE;
15342 player->is_active = TRUE;
15344 if (!(IN_LEV_FIELD(nextx, nexty) &&
15345 (IS_FREE(nextx, nexty) ||
15346 (IS_SB_ELEMENT(element) &&
15347 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15348 (IS_CUSTOM_ELEMENT(element) &&
15349 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15350 return MP_NO_ACTION;
15352 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15353 return MP_NO_ACTION;
15355 if (player->push_delay == -1) /* new pushing; restart delay */
15356 player->push_delay = 0;
15358 if (player->push_delay < player->push_delay_value &&
15359 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15360 element != EL_SPRING && element != EL_BALLOON)
15362 /* make sure that there is no move delay before next try to push */
15363 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15364 player->move_delay = 0;
15366 return MP_NO_ACTION;
15369 if (IS_CUSTOM_ELEMENT(element) &&
15370 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15372 if (!DigFieldByCE(nextx, nexty, element))
15373 return MP_NO_ACTION;
15376 if (IS_SB_ELEMENT(element))
15378 if (element == EL_SOKOBAN_FIELD_FULL)
15380 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15381 local_player->sokobanfields_still_needed++;
15384 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15386 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15387 local_player->sokobanfields_still_needed--;
15390 Feld[x][y] = EL_SOKOBAN_OBJECT;
15392 if (Back[x][y] == Back[nextx][nexty])
15393 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15394 else if (Back[x][y] != 0)
15395 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15398 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15402 if (local_player->sokobanfields_still_needed == 0 &&
15403 (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15405 if (local_player->sokobanfields_still_needed == 0 &&
15406 game.emulation == EMU_SOKOBAN)
15409 PlayerWins(player);
15411 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15415 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15417 InitMovingField(x, y, move_direction);
15418 GfxAction[x][y] = ACTION_PUSHING;
15420 if (mode == DF_SNAP)
15421 ContinueMoving(x, y);
15423 MovPos[x][y] = (dx != 0 ? dx : dy);
15425 Pushed[x][y] = TRUE;
15426 Pushed[nextx][nexty] = TRUE;
15428 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15429 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15431 player->push_delay_value = -1; /* get new value later */
15433 /* check for element change _after_ element has been pushed */
15434 if (game.use_change_when_pushing_bug)
15436 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15437 player->index_bit, dig_side);
15438 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15439 player->index_bit, dig_side);
15442 else if (IS_SWITCHABLE(element))
15444 if (PLAYER_SWITCHING(player, x, y))
15446 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15447 player->index_bit, dig_side);
15452 player->is_switching = TRUE;
15453 player->switch_x = x;
15454 player->switch_y = y;
15456 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15458 if (element == EL_ROBOT_WHEEL)
15460 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15464 game.robot_wheel_active = TRUE;
15466 TEST_DrawLevelField(x, y);
15468 else if (element == EL_SP_TERMINAL)
15472 SCAN_PLAYFIELD(xx, yy)
15474 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15476 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15477 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15480 else if (IS_BELT_SWITCH(element))
15482 ToggleBeltSwitch(x, y);
15484 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15485 element == EL_SWITCHGATE_SWITCH_DOWN ||
15486 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15487 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15489 ToggleSwitchgateSwitch(x, y);
15491 else if (element == EL_LIGHT_SWITCH ||
15492 element == EL_LIGHT_SWITCH_ACTIVE)
15494 ToggleLightSwitch(x, y);
15496 else if (element == EL_TIMEGATE_SWITCH ||
15497 element == EL_DC_TIMEGATE_SWITCH)
15499 ActivateTimegateSwitch(x, y);
15501 else if (element == EL_BALLOON_SWITCH_LEFT ||
15502 element == EL_BALLOON_SWITCH_RIGHT ||
15503 element == EL_BALLOON_SWITCH_UP ||
15504 element == EL_BALLOON_SWITCH_DOWN ||
15505 element == EL_BALLOON_SWITCH_NONE ||
15506 element == EL_BALLOON_SWITCH_ANY)
15508 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15509 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15510 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15511 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15512 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15515 else if (element == EL_LAMP)
15517 Feld[x][y] = EL_LAMP_ACTIVE;
15518 local_player->lights_still_needed--;
15520 ResetGfxAnimation(x, y);
15521 TEST_DrawLevelField(x, y);
15523 else if (element == EL_TIME_ORB_FULL)
15525 Feld[x][y] = EL_TIME_ORB_EMPTY;
15527 if (level.time > 0 || level.use_time_orb_bug)
15529 TimeLeft += level.time_orb_time;
15532 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15534 DisplayGameControlValues();
15536 DrawGameValue_Time(TimeLeft);
15540 ResetGfxAnimation(x, y);
15541 TEST_DrawLevelField(x, y);
15543 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15544 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15548 game.ball_state = !game.ball_state;
15550 SCAN_PLAYFIELD(xx, yy)
15552 int e = Feld[xx][yy];
15554 if (game.ball_state)
15556 if (e == EL_EMC_MAGIC_BALL)
15557 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15558 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15559 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15563 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15564 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15565 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15566 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15571 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15572 player->index_bit, dig_side);
15574 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15575 player->index_bit, dig_side);
15577 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15578 player->index_bit, dig_side);
15584 if (!PLAYER_SWITCHING(player, x, y))
15586 player->is_switching = TRUE;
15587 player->switch_x = x;
15588 player->switch_y = y;
15590 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15591 player->index_bit, dig_side);
15592 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15593 player->index_bit, dig_side);
15595 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15596 player->index_bit, dig_side);
15597 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15598 player->index_bit, dig_side);
15601 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15602 player->index_bit, dig_side);
15603 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15604 player->index_bit, dig_side);
15606 return MP_NO_ACTION;
15609 player->push_delay = -1;
15611 if (is_player) /* function can also be called by EL_PENGUIN */
15613 if (Feld[x][y] != element) /* really digged/collected something */
15615 player->is_collecting = !player->is_digging;
15616 player->is_active = TRUE;
15623 static boolean DigFieldByCE(int x, int y, int digging_element)
15625 int element = Feld[x][y];
15627 if (!IS_FREE(x, y))
15629 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15630 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15633 /* no element can dig solid indestructible elements */
15634 if (IS_INDESTRUCTIBLE(element) &&
15635 !IS_DIGGABLE(element) &&
15636 !IS_COLLECTIBLE(element))
15639 if (AmoebaNr[x][y] &&
15640 (element == EL_AMOEBA_FULL ||
15641 element == EL_BD_AMOEBA ||
15642 element == EL_AMOEBA_GROWING))
15644 AmoebaCnt[AmoebaNr[x][y]]--;
15645 AmoebaCnt2[AmoebaNr[x][y]]--;
15648 if (IS_MOVING(x, y))
15649 RemoveMovingField(x, y);
15653 TEST_DrawLevelField(x, y);
15656 /* if digged element was about to explode, prevent the explosion */
15657 ExplodeField[x][y] = EX_TYPE_NONE;
15659 PlayLevelSoundAction(x, y, action);
15662 Store[x][y] = EL_EMPTY;
15665 /* this makes it possible to leave the removed element again */
15666 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15667 Store[x][y] = element;
15669 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15671 int move_leave_element = element_info[digging_element].move_leave_element;
15673 /* this makes it possible to leave the removed element again */
15674 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15675 element : move_leave_element);
15682 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15684 int jx = player->jx, jy = player->jy;
15685 int x = jx + dx, y = jy + dy;
15686 int snap_direction = (dx == -1 ? MV_LEFT :
15687 dx == +1 ? MV_RIGHT :
15689 dy == +1 ? MV_DOWN : MV_NONE);
15690 boolean can_continue_snapping = (level.continuous_snapping &&
15691 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15693 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15696 if (!player->active || !IN_LEV_FIELD(x, y))
15704 if (player->MovPos == 0)
15705 player->is_pushing = FALSE;
15707 player->is_snapping = FALSE;
15709 if (player->MovPos == 0)
15711 player->is_moving = FALSE;
15712 player->is_digging = FALSE;
15713 player->is_collecting = FALSE;
15719 #if USE_NEW_CONTINUOUS_SNAPPING
15720 /* prevent snapping with already pressed snap key when not allowed */
15721 if (player->is_snapping && !can_continue_snapping)
15724 if (player->is_snapping)
15728 player->MovDir = snap_direction;
15730 if (player->MovPos == 0)
15732 player->is_moving = FALSE;
15733 player->is_digging = FALSE;
15734 player->is_collecting = FALSE;
15737 player->is_dropping = FALSE;
15738 player->is_dropping_pressed = FALSE;
15739 player->drop_pressed_delay = 0;
15741 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15744 player->is_snapping = TRUE;
15745 player->is_active = TRUE;
15747 if (player->MovPos == 0)
15749 player->is_moving = FALSE;
15750 player->is_digging = FALSE;
15751 player->is_collecting = FALSE;
15754 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15755 TEST_DrawLevelField(player->last_jx, player->last_jy);
15757 TEST_DrawLevelField(x, y);
15762 static boolean DropElement(struct PlayerInfo *player)
15764 int old_element, new_element;
15765 int dropx = player->jx, dropy = player->jy;
15766 int drop_direction = player->MovDir;
15767 int drop_side = drop_direction;
15769 int drop_element = get_next_dropped_element(player);
15771 int drop_element = (player->inventory_size > 0 ?
15772 player->inventory_element[player->inventory_size - 1] :
15773 player->inventory_infinite_element != EL_UNDEFINED ?
15774 player->inventory_infinite_element :
15775 player->dynabombs_left > 0 ?
15776 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15780 player->is_dropping_pressed = TRUE;
15782 /* do not drop an element on top of another element; when holding drop key
15783 pressed without moving, dropped element must move away before the next
15784 element can be dropped (this is especially important if the next element
15785 is dynamite, which can be placed on background for historical reasons) */
15786 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15789 if (IS_THROWABLE(drop_element))
15791 dropx += GET_DX_FROM_DIR(drop_direction);
15792 dropy += GET_DY_FROM_DIR(drop_direction);
15794 if (!IN_LEV_FIELD(dropx, dropy))
15798 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15799 new_element = drop_element; /* default: no change when dropping */
15801 /* check if player is active, not moving and ready to drop */
15802 if (!player->active || player->MovPos || player->drop_delay > 0)
15805 /* check if player has anything that can be dropped */
15806 if (new_element == EL_UNDEFINED)
15809 /* check if drop key was pressed long enough for EM style dynamite */
15810 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15813 /* check if anything can be dropped at the current position */
15814 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15817 /* collected custom elements can only be dropped on empty fields */
15818 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15821 if (old_element != EL_EMPTY)
15822 Back[dropx][dropy] = old_element; /* store old element on this field */
15824 ResetGfxAnimation(dropx, dropy);
15825 ResetRandomAnimationValue(dropx, dropy);
15827 if (player->inventory_size > 0 ||
15828 player->inventory_infinite_element != EL_UNDEFINED)
15830 if (player->inventory_size > 0)
15832 player->inventory_size--;
15834 DrawGameDoorValues();
15836 if (new_element == EL_DYNAMITE)
15837 new_element = EL_DYNAMITE_ACTIVE;
15838 else if (new_element == EL_EM_DYNAMITE)
15839 new_element = EL_EM_DYNAMITE_ACTIVE;
15840 else if (new_element == EL_SP_DISK_RED)
15841 new_element = EL_SP_DISK_RED_ACTIVE;
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);
15852 /* needed if previous element just changed to "empty" in the last frame */
15853 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15855 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15856 player->index_bit, drop_side);
15857 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15859 player->index_bit, drop_side);
15861 TestIfElementTouchesCustomElement(dropx, dropy);
15863 else /* player is dropping a dyna bomb */
15865 player->dynabombs_left--;
15867 Feld[dropx][dropy] = new_element;
15869 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15870 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15871 el2img(Feld[dropx][dropy]), 0);
15873 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15876 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15877 InitField_WithBug1(dropx, dropy, FALSE);
15879 new_element = Feld[dropx][dropy]; /* element might have changed */
15881 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15882 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15884 int move_direction, nextx, nexty;
15886 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15887 MovDir[dropx][dropy] = drop_direction;
15889 move_direction = MovDir[dropx][dropy];
15890 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15891 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15893 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15895 #if USE_FIX_IMPACT_COLLISION
15896 /* do not cause impact style collision by dropping elements that can fall */
15897 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15899 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15903 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15904 player->is_dropping = TRUE;
15906 player->drop_pressed_delay = 0;
15907 player->is_dropping_pressed = FALSE;
15909 player->drop_x = dropx;
15910 player->drop_y = dropy;
15915 /* ------------------------------------------------------------------------- */
15916 /* game sound playing functions */
15917 /* ------------------------------------------------------------------------- */
15919 static int *loop_sound_frame = NULL;
15920 static int *loop_sound_volume = NULL;
15922 void InitPlayLevelSound()
15924 int num_sounds = getSoundListSize();
15926 checked_free(loop_sound_frame);
15927 checked_free(loop_sound_volume);
15929 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15930 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15933 static void PlayLevelSound(int x, int y, int nr)
15935 int sx = SCREENX(x), sy = SCREENY(y);
15936 int volume, stereo_position;
15937 int max_distance = 8;
15938 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15940 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15941 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15944 if (!IN_LEV_FIELD(x, y) ||
15945 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15946 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15949 volume = SOUND_MAX_VOLUME;
15951 if (!IN_SCR_FIELD(sx, sy))
15953 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15954 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15956 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15959 stereo_position = (SOUND_MAX_LEFT +
15960 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15961 (SCR_FIELDX + 2 * max_distance));
15963 if (IS_LOOP_SOUND(nr))
15965 /* This assures that quieter loop sounds do not overwrite louder ones,
15966 while restarting sound volume comparison with each new game frame. */
15968 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15971 loop_sound_volume[nr] = volume;
15972 loop_sound_frame[nr] = FrameCounter;
15975 PlaySoundExt(nr, volume, stereo_position, type);
15978 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15980 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15981 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15982 y < LEVELY(BY1) ? LEVELY(BY1) :
15983 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15987 static void PlayLevelSoundAction(int x, int y, int action)
15989 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15992 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15994 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15996 if (sound_effect != SND_UNDEFINED)
15997 PlayLevelSound(x, y, sound_effect);
16000 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16003 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16005 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16006 PlayLevelSound(x, y, sound_effect);
16009 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16011 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16013 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16014 PlayLevelSound(x, y, sound_effect);
16017 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16019 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16021 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16022 StopSound(sound_effect);
16025 static void PlayLevelMusic()
16027 if (levelset.music[level_nr] != MUS_UNDEFINED)
16028 PlayMusic(levelset.music[level_nr]); /* from config file */
16030 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
16033 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16035 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16036 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16037 int x = xx - 1 - offset;
16038 int y = yy - 1 - offset;
16043 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16047 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16051 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16055 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16059 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16063 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16067 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16070 case SAMPLE_android_clone:
16071 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16074 case SAMPLE_android_move:
16075 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16078 case SAMPLE_spring:
16079 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16083 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16087 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16090 case SAMPLE_eater_eat:
16091 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16095 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16098 case SAMPLE_collect:
16099 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16102 case SAMPLE_diamond:
16103 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16106 case SAMPLE_squash:
16107 /* !!! CHECK THIS !!! */
16109 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16111 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16115 case SAMPLE_wonderfall:
16116 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16120 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16124 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16128 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16132 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16136 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16140 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16143 case SAMPLE_wonder:
16144 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16148 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16151 case SAMPLE_exit_open:
16152 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16155 case SAMPLE_exit_leave:
16156 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16159 case SAMPLE_dynamite:
16160 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16164 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16168 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16172 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16176 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16180 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16184 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16188 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16194 void ChangeTime(int value)
16196 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16200 /* EMC game engine uses value from time counter of RND game engine */
16201 level.native_em_level->lev->time = *time;
16203 DrawGameValue_Time(*time);
16206 void RaiseScore(int value)
16208 /* EMC game engine and RND game engine have separate score counters */
16209 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16210 &level.native_em_level->lev->score : &local_player->score);
16214 DrawGameValue_Score(*score);
16218 void RaiseScore(int value)
16220 local_player->score += value;
16223 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16225 DisplayGameControlValues();
16227 DrawGameValue_Score(local_player->score);
16231 void RaiseScoreElement(int element)
16236 case EL_BD_DIAMOND:
16237 case EL_EMERALD_YELLOW:
16238 case EL_EMERALD_RED:
16239 case EL_EMERALD_PURPLE:
16240 case EL_SP_INFOTRON:
16241 RaiseScore(level.score[SC_EMERALD]);
16244 RaiseScore(level.score[SC_DIAMOND]);
16247 RaiseScore(level.score[SC_CRYSTAL]);
16250 RaiseScore(level.score[SC_PEARL]);
16253 case EL_BD_BUTTERFLY:
16254 case EL_SP_ELECTRON:
16255 RaiseScore(level.score[SC_BUG]);
16258 case EL_BD_FIREFLY:
16259 case EL_SP_SNIKSNAK:
16260 RaiseScore(level.score[SC_SPACESHIP]);
16263 case EL_DARK_YAMYAM:
16264 RaiseScore(level.score[SC_YAMYAM]);
16267 RaiseScore(level.score[SC_ROBOT]);
16270 RaiseScore(level.score[SC_PACMAN]);
16273 RaiseScore(level.score[SC_NUT]);
16276 case EL_EM_DYNAMITE:
16277 case EL_SP_DISK_RED:
16278 case EL_DYNABOMB_INCREASE_NUMBER:
16279 case EL_DYNABOMB_INCREASE_SIZE:
16280 case EL_DYNABOMB_INCREASE_POWER:
16281 RaiseScore(level.score[SC_DYNAMITE]);
16283 case EL_SHIELD_NORMAL:
16284 case EL_SHIELD_DEADLY:
16285 RaiseScore(level.score[SC_SHIELD]);
16287 case EL_EXTRA_TIME:
16288 RaiseScore(level.extra_time_score);
16302 case EL_DC_KEY_WHITE:
16303 RaiseScore(level.score[SC_KEY]);
16306 RaiseScore(element_info[element].collect_score);
16311 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16313 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16315 #if defined(NETWORK_AVALIABLE)
16316 if (options.network)
16317 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16326 FadeSkipNextFadeIn();
16328 fading = fading_none;
16332 OpenDoor(DOOR_CLOSE_1);
16335 game_status = GAME_MODE_MAIN;
16338 DrawAndFadeInMainMenu(REDRAW_FIELD);
16346 FadeOut(REDRAW_FIELD);
16349 game_status = GAME_MODE_MAIN;
16351 DrawAndFadeInMainMenu(REDRAW_FIELD);
16355 else /* continue playing the game */
16357 if (tape.playing && tape.deactivate_display)
16358 TapeDeactivateDisplayOff(TRUE);
16360 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16362 if (tape.playing && tape.deactivate_display)
16363 TapeDeactivateDisplayOn();
16367 void RequestQuitGame(boolean ask_if_really_quit)
16369 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16370 boolean skip_request = AllPlayersGone || quick_quit;
16372 RequestQuitGameExt(skip_request, quick_quit,
16373 "Do you really want to quit the game ?");
16377 /* ------------------------------------------------------------------------- */
16378 /* random generator functions */
16379 /* ------------------------------------------------------------------------- */
16381 unsigned int InitEngineRandom_RND(long seed)
16383 game.num_random_calls = 0;
16386 unsigned int rnd_seed = InitEngineRandom(seed);
16388 printf("::: START RND: %d\n", rnd_seed);
16393 return InitEngineRandom(seed);
16399 unsigned int RND(int max)
16403 game.num_random_calls++;
16405 return GetEngineRandom(max);
16412 /* ------------------------------------------------------------------------- */
16413 /* game engine snapshot handling functions */
16414 /* ------------------------------------------------------------------------- */
16416 struct EngineSnapshotInfo
16418 /* runtime values for custom element collect score */
16419 int collect_score[NUM_CUSTOM_ELEMENTS];
16421 /* runtime values for group element choice position */
16422 int choice_pos[NUM_GROUP_ELEMENTS];
16424 /* runtime values for belt position animations */
16425 int belt_graphic[4 * NUM_BELT_PARTS];
16426 int belt_anim_mode[4 * NUM_BELT_PARTS];
16429 static struct EngineSnapshotInfo engine_snapshot_rnd;
16430 static char *snapshot_level_identifier = NULL;
16431 static int snapshot_level_nr = -1;
16433 static void SaveEngineSnapshotValues_RND()
16435 static int belt_base_active_element[4] =
16437 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16438 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16439 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16440 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16444 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16446 int element = EL_CUSTOM_START + i;
16448 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16451 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16453 int element = EL_GROUP_START + i;
16455 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16458 for (i = 0; i < 4; i++)
16460 for (j = 0; j < NUM_BELT_PARTS; j++)
16462 int element = belt_base_active_element[i] + j;
16463 int graphic = el2img(element);
16464 int anim_mode = graphic_info[graphic].anim_mode;
16466 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16467 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16472 static void LoadEngineSnapshotValues_RND()
16474 unsigned long num_random_calls = game.num_random_calls;
16477 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16479 int element = EL_CUSTOM_START + i;
16481 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16484 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16486 int element = EL_GROUP_START + i;
16488 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16491 for (i = 0; i < 4; i++)
16493 for (j = 0; j < NUM_BELT_PARTS; j++)
16495 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16496 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16498 graphic_info[graphic].anim_mode = anim_mode;
16502 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16504 InitRND(tape.random_seed);
16505 for (i = 0; i < num_random_calls; i++)
16509 if (game.num_random_calls != num_random_calls)
16511 Error(ERR_INFO, "number of random calls out of sync");
16512 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16513 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16514 Error(ERR_EXIT, "this should not happen -- please debug");
16518 void SaveEngineSnapshot()
16520 /* do not save snapshots from editor */
16521 if (level_editor_test_game)
16524 /* free previous snapshot buffers, if needed */
16525 FreeEngineSnapshotBuffers();
16527 /* copy some special values to a structure better suited for the snapshot */
16529 SaveEngineSnapshotValues_RND();
16530 SaveEngineSnapshotValues_EM();
16531 SaveEngineSnapshotValues_SP();
16533 /* save values stored in special snapshot structure */
16535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16536 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16539 /* save further RND engine values */
16541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16543 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16569 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16572 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16576 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16579 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16589 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16590 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16591 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16593 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16594 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16596 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16597 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16598 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16599 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16600 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16602 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16603 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16605 /* save level identification information */
16607 setString(&snapshot_level_identifier, leveldir_current->identifier);
16608 snapshot_level_nr = level_nr;
16611 ListNode *node = engine_snapshot_list_rnd;
16614 while (node != NULL)
16616 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16621 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16625 void LoadEngineSnapshot()
16627 /* restore generically stored snapshot buffers */
16629 LoadEngineSnapshotBuffers();
16631 /* restore special values from snapshot structure */
16633 LoadEngineSnapshotValues_RND();
16634 LoadEngineSnapshotValues_EM();
16635 LoadEngineSnapshotValues_SP();
16638 boolean CheckEngineSnapshot()
16640 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16641 snapshot_level_nr == level_nr);
16645 /* ---------- new game button stuff ---------------------------------------- */
16647 /* graphic position values for game buttons */
16648 #define GAME_BUTTON_XSIZE 30
16649 #define GAME_BUTTON_YSIZE 30
16650 #define GAME_BUTTON_XPOS 5
16651 #define GAME_BUTTON_YPOS 215
16652 #define SOUND_BUTTON_XPOS 5
16653 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16655 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16656 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16657 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16658 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16659 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16660 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16668 } gamebutton_info[NUM_GAME_BUTTONS] =
16672 &game.button.stop.x, &game.button.stop.y,
16673 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16678 &game.button.pause.x, &game.button.pause.y,
16679 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16680 GAME_CTRL_ID_PAUSE,
16684 &game.button.play.x, &game.button.play.y,
16685 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16690 &game.button.sound_music.x, &game.button.sound_music.y,
16691 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16692 SOUND_CTRL_ID_MUSIC,
16693 "background music on/off"
16696 &game.button.sound_loops.x, &game.button.sound_loops.y,
16697 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16698 SOUND_CTRL_ID_LOOPS,
16699 "sound loops on/off"
16702 &game.button.sound_simple.x,&game.button.sound_simple.y,
16703 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16704 SOUND_CTRL_ID_SIMPLE,
16705 "normal sounds on/off"
16709 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16714 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16715 GAME_CTRL_ID_PAUSE,
16719 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16724 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16725 SOUND_CTRL_ID_MUSIC,
16726 "background music on/off"
16729 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16730 SOUND_CTRL_ID_LOOPS,
16731 "sound loops on/off"
16734 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16735 SOUND_CTRL_ID_SIMPLE,
16736 "normal sounds on/off"
16741 void CreateGameButtons()
16745 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16747 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16748 struct GadgetInfo *gi;
16751 unsigned long event_mask;
16753 int gd_xoffset, gd_yoffset;
16754 int gd_x1, gd_x2, gd_y1, gd_y2;
16757 x = DX + *gamebutton_info[i].x;
16758 y = DY + *gamebutton_info[i].y;
16759 gd_xoffset = gamebutton_info[i].gd_x;
16760 gd_yoffset = gamebutton_info[i].gd_y;
16761 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16762 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16764 if (id == GAME_CTRL_ID_STOP ||
16765 id == GAME_CTRL_ID_PAUSE ||
16766 id == GAME_CTRL_ID_PLAY)
16768 button_type = GD_TYPE_NORMAL_BUTTON;
16770 event_mask = GD_EVENT_RELEASED;
16771 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16772 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16776 button_type = GD_TYPE_CHECK_BUTTON;
16778 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16779 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16780 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16781 event_mask = GD_EVENT_PRESSED;
16782 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16783 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16786 gi = CreateGadget(GDI_CUSTOM_ID, id,
16787 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16792 GDI_X, DX + gd_xoffset,
16793 GDI_Y, DY + gd_yoffset,
16795 GDI_WIDTH, GAME_BUTTON_XSIZE,
16796 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16797 GDI_TYPE, button_type,
16798 GDI_STATE, GD_BUTTON_UNPRESSED,
16799 GDI_CHECKED, checked,
16800 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16801 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16802 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16803 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16804 GDI_DIRECT_DRAW, FALSE,
16805 GDI_EVENT_MASK, event_mask,
16806 GDI_CALLBACK_ACTION, HandleGameButtons,
16810 Error(ERR_EXIT, "cannot create gadget");
16812 game_gadget[id] = gi;
16816 void FreeGameButtons()
16820 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16821 FreeGadget(game_gadget[i]);
16824 static void MapGameButtons()
16828 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16829 MapGadget(game_gadget[i]);
16832 void UnmapGameButtons()
16836 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16837 UnmapGadget(game_gadget[i]);
16840 void RedrawGameButtons()
16844 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16845 RedrawGadget(game_gadget[i]);
16848 static void HandleGameButtons(struct GadgetInfo *gi)
16850 int id = gi->custom_id;
16852 if (game_status != GAME_MODE_PLAYING)
16857 case GAME_CTRL_ID_STOP:
16861 RequestQuitGame(TRUE);
16864 case GAME_CTRL_ID_PAUSE:
16865 if (options.network)
16867 #if defined(NETWORK_AVALIABLE)
16869 SendToServer_ContinuePlaying();
16871 SendToServer_PausePlaying();
16875 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16878 case GAME_CTRL_ID_PLAY:
16881 #if defined(NETWORK_AVALIABLE)
16882 if (options.network)
16883 SendToServer_ContinuePlaying();
16887 tape.pausing = FALSE;
16888 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16893 case SOUND_CTRL_ID_MUSIC:
16894 if (setup.sound_music)
16896 setup.sound_music = FALSE;
16899 else if (audio.music_available)
16901 setup.sound = setup.sound_music = TRUE;
16903 SetAudioMode(setup.sound);
16909 case SOUND_CTRL_ID_LOOPS:
16910 if (setup.sound_loops)
16911 setup.sound_loops = FALSE;
16912 else if (audio.loops_available)
16914 setup.sound = setup.sound_loops = TRUE;
16915 SetAudioMode(setup.sound);
16919 case SOUND_CTRL_ID_SIMPLE:
16920 if (setup.sound_simple)
16921 setup.sound_simple = FALSE;
16922 else if (audio.sound_available)
16924 setup.sound = setup.sound_simple = TRUE;
16925 SetAudioMode(setup.sound);