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)
62 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
70 /* for MovePlayer() */
71 #define MP_NO_ACTION 0
74 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
76 /* for ScrollPlayer() */
78 #define SCROLL_GO_ON 1
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START 0
82 #define EX_TYPE_NONE 0
83 #define EX_TYPE_NORMAL (1 << 0)
84 #define EX_TYPE_CENTER (1 << 1)
85 #define EX_TYPE_BORDER (1 << 2)
86 #define EX_TYPE_CROSS (1 << 3)
87 #define EX_TYPE_DYNA (1 << 4)
88 #define EX_TYPE_SINGLE_TILE (EX_TYPE_CENTER | EX_TYPE_BORDER)
90 #define PANEL_OFF() (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p) ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p) (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p) (DY + ALIGNED_TEXT_YPOS(p))
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1 (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2 (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1 (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2 (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME (PANEL_XPOS(game.panel.time))
111 #define YY_TIME (PANEL_YPOS(game.panel.time))
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1 (DX + XX_LEVEL1)
115 #define DX_LEVEL2 (DX + XX_LEVEL2)
116 #define DX_LEVEL (DX + XX_LEVEL)
117 #define DY_LEVEL (DY + YY_LEVEL)
118 #define DX_EMERALDS (DX + XX_EMERALDS)
119 #define DY_EMERALDS (DY + YY_EMERALDS)
120 #define DX_DYNAMITE (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE (DY + YY_DYNAMITE)
122 #define DX_KEYS (DX + XX_KEYS)
123 #define DY_KEYS (DY + YY_KEYS)
124 #define DX_SCORE (DX + XX_SCORE)
125 #define DY_SCORE (DY + YY_SCORE)
126 #define DX_TIME1 (DX + XX_TIME1)
127 #define DX_TIME2 (DX + XX_TIME2)
128 #define DX_TIME (DX + XX_TIME)
129 #define DY_TIME (DY + YY_TIME)
132 /* game panel display and control definitions */
134 #define GAME_PANEL_LEVEL_NUMBER 0
135 #define GAME_PANEL_GEMS 1
136 #define GAME_PANEL_INVENTORY_COUNT 2
137 #define GAME_PANEL_INVENTORY_FIRST_1 3
138 #define GAME_PANEL_INVENTORY_FIRST_2 4
139 #define GAME_PANEL_INVENTORY_FIRST_3 5
140 #define GAME_PANEL_INVENTORY_FIRST_4 6
141 #define GAME_PANEL_INVENTORY_FIRST_5 7
142 #define GAME_PANEL_INVENTORY_FIRST_6 8
143 #define GAME_PANEL_INVENTORY_FIRST_7 9
144 #define GAME_PANEL_INVENTORY_FIRST_8 10
145 #define GAME_PANEL_INVENTORY_LAST_1 11
146 #define GAME_PANEL_INVENTORY_LAST_2 12
147 #define GAME_PANEL_INVENTORY_LAST_3 13
148 #define GAME_PANEL_INVENTORY_LAST_4 14
149 #define GAME_PANEL_INVENTORY_LAST_5 15
150 #define GAME_PANEL_INVENTORY_LAST_6 16
151 #define GAME_PANEL_INVENTORY_LAST_7 17
152 #define GAME_PANEL_INVENTORY_LAST_8 18
153 #define GAME_PANEL_KEY_1 19
154 #define GAME_PANEL_KEY_2 20
155 #define GAME_PANEL_KEY_3 21
156 #define GAME_PANEL_KEY_4 22
157 #define GAME_PANEL_KEY_5 23
158 #define GAME_PANEL_KEY_6 24
159 #define GAME_PANEL_KEY_7 25
160 #define GAME_PANEL_KEY_8 26
161 #define GAME_PANEL_KEY_WHITE 27
162 #define GAME_PANEL_KEY_WHITE_COUNT 28
163 #define GAME_PANEL_SCORE 29
164 #define GAME_PANEL_TIME 30
165 #define GAME_PANEL_TIME_HH 31
166 #define GAME_PANEL_TIME_MM 32
167 #define GAME_PANEL_TIME_SS 33
168 #define GAME_PANEL_SHIELD_NORMAL 34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME 35
170 #define GAME_PANEL_SHIELD_DEADLY 36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME 37
172 #define GAME_PANEL_EXIT 38
173 #define GAME_PANEL_EMC_MAGIC_BALL 39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 40
175 #define GAME_PANEL_LIGHT_SWITCH 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME 42
177 #define GAME_PANEL_TIMEGATE_SWITCH 43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH 45
180 #define GAME_PANEL_EMC_LENSES 46
181 #define GAME_PANEL_EMC_LENSES_TIME 47
182 #define GAME_PANEL_EMC_MAGNIFIER 48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME 49
184 #define GAME_PANEL_BALLOON_SWITCH 50
185 #define GAME_PANEL_DYNABOMB_NUMBER 51
186 #define GAME_PANEL_DYNABOMB_SIZE 52
187 #define GAME_PANEL_DYNABOMB_POWER 53
188 #define GAME_PANEL_PENGUINS 54
189 #define GAME_PANEL_SOKOBAN_OBJECTS 55
190 #define GAME_PANEL_SOKOBAN_FIELDS 56
191 #define GAME_PANEL_ROBOT_WHEEL 57
192 #define GAME_PANEL_CONVEYOR_BELT_1 58
193 #define GAME_PANEL_CONVEYOR_BELT_2 59
194 #define GAME_PANEL_CONVEYOR_BELT_3 60
195 #define GAME_PANEL_CONVEYOR_BELT_4 61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 65
200 #define GAME_PANEL_MAGIC_WALL 66
201 #define GAME_PANEL_MAGIC_WALL_TIME 67
202 #define GAME_PANEL_GRAVITY_STATE 68
203 #define GAME_PANEL_GRAPHIC_1 69
204 #define GAME_PANEL_GRAPHIC_2 70
205 #define GAME_PANEL_GRAPHIC_3 71
206 #define GAME_PANEL_GRAPHIC_4 72
207 #define GAME_PANEL_GRAPHIC_5 73
208 #define GAME_PANEL_GRAPHIC_6 74
209 #define GAME_PANEL_GRAPHIC_7 75
210 #define GAME_PANEL_GRAPHIC_8 76
211 #define GAME_PANEL_ELEMENT_1 77
212 #define GAME_PANEL_ELEMENT_2 78
213 #define GAME_PANEL_ELEMENT_3 79
214 #define GAME_PANEL_ELEMENT_4 80
215 #define GAME_PANEL_ELEMENT_5 81
216 #define GAME_PANEL_ELEMENT_6 82
217 #define GAME_PANEL_ELEMENT_7 83
218 #define GAME_PANEL_ELEMENT_8 84
219 #define GAME_PANEL_ELEMENT_COUNT_1 85
220 #define GAME_PANEL_ELEMENT_COUNT_2 86
221 #define GAME_PANEL_ELEMENT_COUNT_3 87
222 #define GAME_PANEL_ELEMENT_COUNT_4 88
223 #define GAME_PANEL_ELEMENT_COUNT_5 89
224 #define GAME_PANEL_ELEMENT_COUNT_6 90
225 #define GAME_PANEL_ELEMENT_COUNT_7 91
226 #define GAME_PANEL_ELEMENT_COUNT_8 92
227 #define GAME_PANEL_CE_SCORE_1 93
228 #define GAME_PANEL_CE_SCORE_2 94
229 #define GAME_PANEL_CE_SCORE_3 95
230 #define GAME_PANEL_CE_SCORE_4 96
231 #define GAME_PANEL_CE_SCORE_5 97
232 #define GAME_PANEL_CE_SCORE_6 98
233 #define GAME_PANEL_CE_SCORE_7 99
234 #define GAME_PANEL_CE_SCORE_8 100
235 #define GAME_PANEL_CE_SCORE_1_ELEMENT 101
236 #define GAME_PANEL_CE_SCORE_2_ELEMENT 102
237 #define GAME_PANEL_CE_SCORE_3_ELEMENT 103
238 #define GAME_PANEL_CE_SCORE_4_ELEMENT 104
239 #define GAME_PANEL_CE_SCORE_5_ELEMENT 105
240 #define GAME_PANEL_CE_SCORE_6_ELEMENT 106
241 #define GAME_PANEL_CE_SCORE_7_ELEMENT 107
242 #define GAME_PANEL_CE_SCORE_8_ELEMENT 108
243 #define GAME_PANEL_PLAYER_NAME 109
244 #define GAME_PANEL_LEVEL_NAME 110
245 #define GAME_PANEL_LEVEL_AUTHOR 111
247 #define NUM_GAME_PANEL_CONTROLS 112
249 struct GamePanelOrderInfo
255 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
257 struct GamePanelControlInfo
261 struct TextPosInfo *pos;
264 int value, last_value;
265 int frame, last_frame;
270 static struct GamePanelControlInfo game_panel_controls[] =
273 GAME_PANEL_LEVEL_NUMBER,
274 &game.panel.level_number,
283 GAME_PANEL_INVENTORY_COUNT,
284 &game.panel.inventory_count,
288 GAME_PANEL_INVENTORY_FIRST_1,
289 &game.panel.inventory_first[0],
293 GAME_PANEL_INVENTORY_FIRST_2,
294 &game.panel.inventory_first[1],
298 GAME_PANEL_INVENTORY_FIRST_3,
299 &game.panel.inventory_first[2],
303 GAME_PANEL_INVENTORY_FIRST_4,
304 &game.panel.inventory_first[3],
308 GAME_PANEL_INVENTORY_FIRST_5,
309 &game.panel.inventory_first[4],
313 GAME_PANEL_INVENTORY_FIRST_6,
314 &game.panel.inventory_first[5],
318 GAME_PANEL_INVENTORY_FIRST_7,
319 &game.panel.inventory_first[6],
323 GAME_PANEL_INVENTORY_FIRST_8,
324 &game.panel.inventory_first[7],
328 GAME_PANEL_INVENTORY_LAST_1,
329 &game.panel.inventory_last[0],
333 GAME_PANEL_INVENTORY_LAST_2,
334 &game.panel.inventory_last[1],
338 GAME_PANEL_INVENTORY_LAST_3,
339 &game.panel.inventory_last[2],
343 GAME_PANEL_INVENTORY_LAST_4,
344 &game.panel.inventory_last[3],
348 GAME_PANEL_INVENTORY_LAST_5,
349 &game.panel.inventory_last[4],
353 GAME_PANEL_INVENTORY_LAST_6,
354 &game.panel.inventory_last[5],
358 GAME_PANEL_INVENTORY_LAST_7,
359 &game.panel.inventory_last[6],
363 GAME_PANEL_INVENTORY_LAST_8,
364 &game.panel.inventory_last[7],
408 GAME_PANEL_KEY_WHITE,
409 &game.panel.key_white,
413 GAME_PANEL_KEY_WHITE_COUNT,
414 &game.panel.key_white_count,
443 GAME_PANEL_SHIELD_NORMAL,
444 &game.panel.shield_normal,
448 GAME_PANEL_SHIELD_NORMAL_TIME,
449 &game.panel.shield_normal_time,
453 GAME_PANEL_SHIELD_DEADLY,
454 &game.panel.shield_deadly,
458 GAME_PANEL_SHIELD_DEADLY_TIME,
459 &game.panel.shield_deadly_time,
468 GAME_PANEL_EMC_MAGIC_BALL,
469 &game.panel.emc_magic_ball,
473 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
474 &game.panel.emc_magic_ball_switch,
478 GAME_PANEL_LIGHT_SWITCH,
479 &game.panel.light_switch,
483 GAME_PANEL_LIGHT_SWITCH_TIME,
484 &game.panel.light_switch_time,
488 GAME_PANEL_TIMEGATE_SWITCH,
489 &game.panel.timegate_switch,
493 GAME_PANEL_TIMEGATE_SWITCH_TIME,
494 &game.panel.timegate_switch_time,
498 GAME_PANEL_SWITCHGATE_SWITCH,
499 &game.panel.switchgate_switch,
503 GAME_PANEL_EMC_LENSES,
504 &game.panel.emc_lenses,
508 GAME_PANEL_EMC_LENSES_TIME,
509 &game.panel.emc_lenses_time,
513 GAME_PANEL_EMC_MAGNIFIER,
514 &game.panel.emc_magnifier,
518 GAME_PANEL_EMC_MAGNIFIER_TIME,
519 &game.panel.emc_magnifier_time,
523 GAME_PANEL_BALLOON_SWITCH,
524 &game.panel.balloon_switch,
528 GAME_PANEL_DYNABOMB_NUMBER,
529 &game.panel.dynabomb_number,
533 GAME_PANEL_DYNABOMB_SIZE,
534 &game.panel.dynabomb_size,
538 GAME_PANEL_DYNABOMB_POWER,
539 &game.panel.dynabomb_power,
544 &game.panel.penguins,
548 GAME_PANEL_SOKOBAN_OBJECTS,
549 &game.panel.sokoban_objects,
553 GAME_PANEL_SOKOBAN_FIELDS,
554 &game.panel.sokoban_fields,
558 GAME_PANEL_ROBOT_WHEEL,
559 &game.panel.robot_wheel,
563 GAME_PANEL_CONVEYOR_BELT_1,
564 &game.panel.conveyor_belt[0],
568 GAME_PANEL_CONVEYOR_BELT_2,
569 &game.panel.conveyor_belt[1],
573 GAME_PANEL_CONVEYOR_BELT_3,
574 &game.panel.conveyor_belt[2],
578 GAME_PANEL_CONVEYOR_BELT_4,
579 &game.panel.conveyor_belt[3],
583 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
584 &game.panel.conveyor_belt_switch[0],
588 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
589 &game.panel.conveyor_belt_switch[1],
593 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
594 &game.panel.conveyor_belt_switch[2],
598 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
599 &game.panel.conveyor_belt_switch[3],
603 GAME_PANEL_MAGIC_WALL,
604 &game.panel.magic_wall,
608 GAME_PANEL_MAGIC_WALL_TIME,
609 &game.panel.magic_wall_time,
613 GAME_PANEL_GRAVITY_STATE,
614 &game.panel.gravity_state,
618 GAME_PANEL_GRAPHIC_1,
619 &game.panel.graphic[0],
623 GAME_PANEL_GRAPHIC_2,
624 &game.panel.graphic[1],
628 GAME_PANEL_GRAPHIC_3,
629 &game.panel.graphic[2],
633 GAME_PANEL_GRAPHIC_4,
634 &game.panel.graphic[3],
638 GAME_PANEL_GRAPHIC_5,
639 &game.panel.graphic[4],
643 GAME_PANEL_GRAPHIC_6,
644 &game.panel.graphic[5],
648 GAME_PANEL_GRAPHIC_7,
649 &game.panel.graphic[6],
653 GAME_PANEL_GRAPHIC_8,
654 &game.panel.graphic[7],
658 GAME_PANEL_ELEMENT_1,
659 &game.panel.element[0],
663 GAME_PANEL_ELEMENT_2,
664 &game.panel.element[1],
668 GAME_PANEL_ELEMENT_3,
669 &game.panel.element[2],
673 GAME_PANEL_ELEMENT_4,
674 &game.panel.element[3],
678 GAME_PANEL_ELEMENT_5,
679 &game.panel.element[4],
683 GAME_PANEL_ELEMENT_6,
684 &game.panel.element[5],
688 GAME_PANEL_ELEMENT_7,
689 &game.panel.element[6],
693 GAME_PANEL_ELEMENT_8,
694 &game.panel.element[7],
698 GAME_PANEL_ELEMENT_COUNT_1,
699 &game.panel.element_count[0],
703 GAME_PANEL_ELEMENT_COUNT_2,
704 &game.panel.element_count[1],
708 GAME_PANEL_ELEMENT_COUNT_3,
709 &game.panel.element_count[2],
713 GAME_PANEL_ELEMENT_COUNT_4,
714 &game.panel.element_count[3],
718 GAME_PANEL_ELEMENT_COUNT_5,
719 &game.panel.element_count[4],
723 GAME_PANEL_ELEMENT_COUNT_6,
724 &game.panel.element_count[5],
728 GAME_PANEL_ELEMENT_COUNT_7,
729 &game.panel.element_count[6],
733 GAME_PANEL_ELEMENT_COUNT_8,
734 &game.panel.element_count[7],
738 GAME_PANEL_CE_SCORE_1,
739 &game.panel.ce_score[0],
743 GAME_PANEL_CE_SCORE_2,
744 &game.panel.ce_score[1],
748 GAME_PANEL_CE_SCORE_3,
749 &game.panel.ce_score[2],
753 GAME_PANEL_CE_SCORE_4,
754 &game.panel.ce_score[3],
758 GAME_PANEL_CE_SCORE_5,
759 &game.panel.ce_score[4],
763 GAME_PANEL_CE_SCORE_6,
764 &game.panel.ce_score[5],
768 GAME_PANEL_CE_SCORE_7,
769 &game.panel.ce_score[6],
773 GAME_PANEL_CE_SCORE_8,
774 &game.panel.ce_score[7],
778 GAME_PANEL_CE_SCORE_1_ELEMENT,
779 &game.panel.ce_score_element[0],
783 GAME_PANEL_CE_SCORE_2_ELEMENT,
784 &game.panel.ce_score_element[1],
788 GAME_PANEL_CE_SCORE_3_ELEMENT,
789 &game.panel.ce_score_element[2],
793 GAME_PANEL_CE_SCORE_4_ELEMENT,
794 &game.panel.ce_score_element[3],
798 GAME_PANEL_CE_SCORE_5_ELEMENT,
799 &game.panel.ce_score_element[4],
803 GAME_PANEL_CE_SCORE_6_ELEMENT,
804 &game.panel.ce_score_element[5],
808 GAME_PANEL_CE_SCORE_7_ELEMENT,
809 &game.panel.ce_score_element[6],
813 GAME_PANEL_CE_SCORE_8_ELEMENT,
814 &game.panel.ce_score_element[7],
818 GAME_PANEL_PLAYER_NAME,
819 &game.panel.player_name,
823 GAME_PANEL_LEVEL_NAME,
824 &game.panel.level_name,
828 GAME_PANEL_LEVEL_AUTHOR,
829 &game.panel.level_author,
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING 3
844 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION 2
846 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF -1
850 #define INITIAL_MOVE_DELAY_ON 0
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED 32
854 #define MOVE_DELAY_NORMAL_SPEED 8
855 #define MOVE_DELAY_HIGH_SPEED 4
856 #define MOVE_DELAY_MAX_SPEED 1
858 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
861 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN (1)
867 #define MOVE_STEPSIZE_MAX (TILEX)
869 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
872 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
874 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
875 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
877 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
879 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
881 (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
883 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
886 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
888 RND((c)->delay_random))
891 #define GET_VALID_RUNTIME_ELEMENT(e) \
892 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
894 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
895 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
896 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
897 (be) + (e) - EL_SELF)
899 #define GET_PLAYER_FROM_BITS(p) \
900 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
903 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
904 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
905 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
906 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
907 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
908 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
909 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
910 RESOLVED_REFERENCE_ELEMENT(be, e) : \
913 #define CAN_GROW_INTO(e) \
914 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
917 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
921 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
922 (CAN_MOVE_INTO_ACID(e) && \
923 Feld[x][y] == EL_ACID) || \
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
927 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
928 (CAN_MOVE_INTO_ACID(e) && \
929 Feld[x][y] == EL_ACID) || \
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
933 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
935 (CAN_MOVE_INTO_ACID(e) && \
936 Feld[x][y] == EL_ACID) || \
937 (DONT_COLLIDE_WITH(e) && \
939 !PLAYER_ENEMY_PROTECTED(x, y))))
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
944 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
948 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
950 #define ANDROID_CAN_CLONE_FIELD(x, y) \
951 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
952 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
966 #define PIG_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
970 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
981 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
986 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
988 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
990 #define CE_ENTER_FIELD_COND(e, x, y) \
991 (!IS_PLAYER(x, y) && \
992 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1000 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP 0
1007 #define GAME_CTRL_ID_PAUSE 1
1008 #define GAME_CTRL_ID_PLAY 2
1009 #define SOUND_CTRL_ID_MUSIC 3
1010 #define SOUND_CTRL_ID_LOOPS 4
1011 #define SOUND_CTRL_ID_SIMPLE 5
1013 #define NUM_GAME_BUTTONS 6
1016 /* forward declaration for internal use */
1018 static void CreateField(int, int, int);
1020 static void ResetGfxAnimation(int, int);
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1030 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1032 static void InitBeltMovement(void);
1033 static void CloseAllOpenTimegates(void);
1034 static void CheckGravityMovement(struct PlayerInfo *);
1035 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1036 static void KillPlayerUnlessEnemyProtected(int, int);
1037 static void KillPlayerUnlessExplosionProtected(int, int);
1039 static void TestIfPlayerTouchesCustomElement(int, int);
1040 static void TestIfElementTouchesCustomElement(int, int);
1041 static void TestIfElementHitsCustomElement(int, int, int);
1043 static void TestIfElementSmashesCustomElement(int, int, int);
1046 static void HandleElementChange(int, int, int);
1047 static void ExecuteCustomElementAction(int, int, int, int);
1048 static boolean ChangeElement(int, int, int, int);
1050 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1051 #define CheckTriggeredElementChange(x, y, e, ev) \
1052 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1053 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1054 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1055 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1056 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1057 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1058 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1060 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1061 #define CheckElementChange(x, y, e, te, ev) \
1062 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1063 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1064 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1065 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1066 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1068 static void PlayLevelSound(int, int, int);
1069 static void PlayLevelSoundNearest(int, int, int);
1070 static void PlayLevelSoundAction(int, int, int);
1071 static void PlayLevelSoundElementAction(int, int, int, int);
1072 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1073 static void PlayLevelSoundActionIfLoop(int, int, int);
1074 static void StopLevelSoundActionIfLoop(int, int, int);
1075 static void PlayLevelMusic();
1077 static void MapGameButtons();
1078 static void HandleGameButtons(struct GadgetInfo *);
1080 int AmoebeNachbarNr(int, int);
1081 void AmoebeUmwandeln(int, int);
1082 void ContinueMoving(int, int);
1083 void Bang(int, int);
1084 void InitMovDir(int, int);
1085 void InitAmoebaNr(int, int);
1086 int NewHiScore(void);
1088 void TestIfGoodThingHitsBadThing(int, int, int);
1089 void TestIfBadThingHitsGoodThing(int, int, int);
1090 void TestIfPlayerTouchesBadThing(int, int);
1091 void TestIfPlayerRunsIntoBadThing(int, int, int);
1092 void TestIfBadThingTouchesPlayer(int, int);
1093 void TestIfBadThingRunsIntoPlayer(int, int, int);
1094 void TestIfFriendTouchesBadThing(int, int);
1095 void TestIfBadThingTouchesFriend(int, int);
1096 void TestIfBadThingTouchesOtherBadThing(int, int);
1098 void KillPlayer(struct PlayerInfo *);
1099 void BuryPlayer(struct PlayerInfo *);
1100 void RemovePlayer(struct PlayerInfo *);
1102 boolean SnapField(struct PlayerInfo *, int, int);
1103 boolean DropElement(struct PlayerInfo *);
1105 static int getInvisibleActiveFromInvisibleElement(int);
1106 static int getInvisibleFromInvisibleActiveElement(int);
1108 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110 /* for detection of endless loops, caused by custom element programming */
1111 /* (using maximal playfield width x 10 is just a rough approximation) */
1112 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1114 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1116 if (recursion_loop_detected) \
1119 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1121 recursion_loop_detected = TRUE; \
1122 recursion_loop_element = (e); \
1125 recursion_loop_depth++; \
1128 #define RECURSION_LOOP_DETECTION_END() \
1130 recursion_loop_depth--; \
1133 static int recursion_loop_depth;
1134 static boolean recursion_loop_detected;
1135 static boolean recursion_loop_element;
1138 /* ------------------------------------------------------------------------- */
1139 /* definition of elements that automatically change to other elements after */
1140 /* a specified time, eventually calling a function when changing */
1141 /* ------------------------------------------------------------------------- */
1143 /* forward declaration for changer functions */
1144 static void InitBuggyBase(int, int);
1145 static void WarnBuggyBase(int, int);
1147 static void InitTrap(int, int);
1148 static void ActivateTrap(int, int);
1149 static void ChangeActiveTrap(int, int);
1151 static void InitRobotWheel(int, int);
1152 static void RunRobotWheel(int, int);
1153 static void StopRobotWheel(int, int);
1155 static void InitTimegateWheel(int, int);
1156 static void RunTimegateWheel(int, int);
1158 static void InitMagicBallDelay(int, int);
1159 static void ActivateMagicBall(int, int);
1161 struct ChangingElementInfo
1166 void (*pre_change_function)(int x, int y);
1167 void (*change_function)(int x, int y);
1168 void (*post_change_function)(int x, int y);
1171 static struct ChangingElementInfo change_delay_list[] =
1206 EL_STEEL_EXIT_OPENING,
1214 EL_STEEL_EXIT_CLOSING,
1215 EL_STEEL_EXIT_CLOSED,
1242 EL_EM_STEEL_EXIT_OPENING,
1243 EL_EM_STEEL_EXIT_OPEN,
1250 EL_EM_STEEL_EXIT_CLOSING,
1254 EL_EM_STEEL_EXIT_CLOSED,
1278 EL_SWITCHGATE_OPENING,
1286 EL_SWITCHGATE_CLOSING,
1287 EL_SWITCHGATE_CLOSED,
1294 EL_TIMEGATE_OPENING,
1302 EL_TIMEGATE_CLOSING,
1311 EL_ACID_SPLASH_LEFT,
1319 EL_ACID_SPLASH_RIGHT,
1328 EL_SP_BUGGY_BASE_ACTIVATING,
1335 EL_SP_BUGGY_BASE_ACTIVATING,
1336 EL_SP_BUGGY_BASE_ACTIVE,
1343 EL_SP_BUGGY_BASE_ACTIVE,
1367 EL_ROBOT_WHEEL_ACTIVE,
1375 EL_TIMEGATE_SWITCH_ACTIVE,
1383 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1384 EL_DC_TIMEGATE_SWITCH,
1391 EL_EMC_MAGIC_BALL_ACTIVE,
1392 EL_EMC_MAGIC_BALL_ACTIVE,
1399 EL_EMC_SPRING_BUMPER_ACTIVE,
1400 EL_EMC_SPRING_BUMPER,
1407 EL_DIAGONAL_SHRINKING,
1415 EL_DIAGONAL_GROWING,
1436 int push_delay_fixed, push_delay_random;
1440 { EL_SPRING, 0, 0 },
1441 { EL_BALLOON, 0, 0 },
1443 { EL_SOKOBAN_OBJECT, 2, 0 },
1444 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1445 { EL_SATELLITE, 2, 0 },
1446 { EL_SP_DISK_YELLOW, 2, 0 },
1448 { EL_UNDEFINED, 0, 0 },
1456 move_stepsize_list[] =
1458 { EL_AMOEBA_DROP, 2 },
1459 { EL_AMOEBA_DROPPING, 2 },
1460 { EL_QUICKSAND_FILLING, 1 },
1461 { EL_QUICKSAND_EMPTYING, 1 },
1462 { EL_QUICKSAND_FAST_FILLING, 2 },
1463 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1464 { EL_MAGIC_WALL_FILLING, 2 },
1465 { EL_MAGIC_WALL_EMPTYING, 2 },
1466 { EL_BD_MAGIC_WALL_FILLING, 2 },
1467 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_DC_MAGIC_WALL_FILLING, 2 },
1469 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1471 { EL_UNDEFINED, 0 },
1479 collect_count_list[] =
1482 { EL_BD_DIAMOND, 1 },
1483 { EL_EMERALD_YELLOW, 1 },
1484 { EL_EMERALD_RED, 1 },
1485 { EL_EMERALD_PURPLE, 1 },
1487 { EL_SP_INFOTRON, 1 },
1491 { EL_UNDEFINED, 0 },
1499 access_direction_list[] =
1501 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1504 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1505 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1506 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1507 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1508 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1509 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1510 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1511 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1513 { EL_SP_PORT_LEFT, MV_RIGHT },
1514 { EL_SP_PORT_RIGHT, MV_LEFT },
1515 { EL_SP_PORT_UP, MV_DOWN },
1516 { EL_SP_PORT_DOWN, MV_UP },
1517 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1518 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1519 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1521 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1522 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1523 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1524 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1525 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1526 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1527 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1528 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1529 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1530 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1531 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1533 { EL_UNDEFINED, MV_NONE }
1536 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1538 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1539 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1540 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1541 IS_JUST_CHANGING(x, y))
1543 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1545 /* static variables for playfield scan mode (scanning forward or backward) */
1546 static int playfield_scan_start_x = 0;
1547 static int playfield_scan_start_y = 0;
1548 static int playfield_scan_delta_x = 1;
1549 static int playfield_scan_delta_y = 1;
1551 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1552 (y) >= 0 && (y) <= lev_fieldy - 1; \
1553 (y) += playfield_scan_delta_y) \
1554 for ((x) = playfield_scan_start_x; \
1555 (x) >= 0 && (x) <= lev_fieldx - 1; \
1556 (x) += playfield_scan_delta_x)
1559 void DEBUG_SetMaximumDynamite()
1563 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1564 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1565 local_player->inventory_element[local_player->inventory_size++] =
1570 static void InitPlayfieldScanModeVars()
1572 if (game.use_reverse_scan_direction)
1574 playfield_scan_start_x = lev_fieldx - 1;
1575 playfield_scan_start_y = lev_fieldy - 1;
1577 playfield_scan_delta_x = -1;
1578 playfield_scan_delta_y = -1;
1582 playfield_scan_start_x = 0;
1583 playfield_scan_start_y = 0;
1585 playfield_scan_delta_x = 1;
1586 playfield_scan_delta_y = 1;
1590 static void InitPlayfieldScanMode(int mode)
1592 game.use_reverse_scan_direction =
1593 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1595 InitPlayfieldScanModeVars();
1598 static int get_move_delay_from_stepsize(int move_stepsize)
1601 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1603 /* make sure that stepsize value is always a power of 2 */
1604 move_stepsize = (1 << log_2(move_stepsize));
1606 return TILEX / move_stepsize;
1609 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1612 int player_nr = player->index_nr;
1613 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1614 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1616 /* do no immediately change move delay -- the player might just be moving */
1617 player->move_delay_value_next = move_delay;
1619 /* information if player can move must be set separately */
1620 player->cannot_move = cannot_move;
1624 player->move_delay = game.initial_move_delay[player_nr];
1625 player->move_delay_value = game.initial_move_delay_value[player_nr];
1627 player->move_delay_value_next = -1;
1629 player->move_delay_reset_counter = 0;
1633 void GetPlayerConfig()
1635 GameFrameDelay = setup.game_frame_delay;
1637 if (!audio.sound_available)
1638 setup.sound_simple = FALSE;
1640 if (!audio.loops_available)
1641 setup.sound_loops = FALSE;
1643 if (!audio.music_available)
1644 setup.sound_music = FALSE;
1646 if (!video.fullscreen_available)
1647 setup.fullscreen = FALSE;
1649 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1651 SetAudioMode(setup.sound);
1655 int GetElementFromGroupElement(int element)
1657 if (IS_GROUP_ELEMENT(element))
1659 struct ElementGroupInfo *group = element_info[element].group;
1660 int last_anim_random_frame = gfx.anim_random_frame;
1663 if (group->choice_mode == ANIM_RANDOM)
1664 gfx.anim_random_frame = RND(group->num_elements_resolved);
1666 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1667 group->choice_mode, 0,
1670 if (group->choice_mode == ANIM_RANDOM)
1671 gfx.anim_random_frame = last_anim_random_frame;
1673 group->choice_pos++;
1675 element = group->element_resolved[element_pos];
1681 static void InitPlayerField(int x, int y, int element, boolean init_game)
1683 if (element == EL_SP_MURPHY)
1687 if (stored_player[0].present)
1689 Feld[x][y] = EL_SP_MURPHY_CLONE;
1695 stored_player[0].use_murphy = TRUE;
1697 if (!level.use_artwork_element[0])
1698 stored_player[0].artwork_element = EL_SP_MURPHY;
1701 Feld[x][y] = EL_PLAYER_1;
1707 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1708 int jx = player->jx, jy = player->jy;
1710 player->present = TRUE;
1712 player->block_last_field = (element == EL_SP_MURPHY ?
1713 level.sp_block_last_field :
1714 level.block_last_field);
1716 /* ---------- initialize player's last field block delay --------------- */
1718 /* always start with reliable default value (no adjustment needed) */
1719 player->block_delay_adjustment = 0;
1721 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1722 if (player->block_last_field && element == EL_SP_MURPHY)
1723 player->block_delay_adjustment = 1;
1725 /* special case 2: in game engines before 3.1.1, blocking was different */
1726 if (game.use_block_last_field_bug)
1727 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1729 if (!options.network || player->connected)
1731 player->active = TRUE;
1733 /* remove potentially duplicate players */
1734 if (StorePlayer[jx][jy] == Feld[x][y])
1735 StorePlayer[jx][jy] = 0;
1737 StorePlayer[x][y] = Feld[x][y];
1741 printf("Player %d activated.\n", player->element_nr);
1742 printf("[Local player is %d and currently %s.]\n",
1743 local_player->element_nr,
1744 local_player->active ? "active" : "not active");
1748 Feld[x][y] = EL_EMPTY;
1750 player->jx = player->last_jx = x;
1751 player->jy = player->last_jy = y;
1755 static void InitField(int x, int y, boolean init_game)
1757 int element = Feld[x][y];
1766 InitPlayerField(x, y, element, init_game);
1769 case EL_SOKOBAN_FIELD_PLAYER:
1770 element = Feld[x][y] = EL_PLAYER_1;
1771 InitField(x, y, init_game);
1773 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1774 InitField(x, y, init_game);
1777 case EL_SOKOBAN_FIELD_EMPTY:
1778 local_player->sokobanfields_still_needed++;
1782 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1783 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1784 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1785 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1786 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1787 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1788 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1789 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1790 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1791 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1800 case EL_SPACESHIP_RIGHT:
1801 case EL_SPACESHIP_UP:
1802 case EL_SPACESHIP_LEFT:
1803 case EL_SPACESHIP_DOWN:
1804 case EL_BD_BUTTERFLY:
1805 case EL_BD_BUTTERFLY_RIGHT:
1806 case EL_BD_BUTTERFLY_UP:
1807 case EL_BD_BUTTERFLY_LEFT:
1808 case EL_BD_BUTTERFLY_DOWN:
1810 case EL_BD_FIREFLY_RIGHT:
1811 case EL_BD_FIREFLY_UP:
1812 case EL_BD_FIREFLY_LEFT:
1813 case EL_BD_FIREFLY_DOWN:
1814 case EL_PACMAN_RIGHT:
1816 case EL_PACMAN_LEFT:
1817 case EL_PACMAN_DOWN:
1819 case EL_YAMYAM_LEFT:
1820 case EL_YAMYAM_RIGHT:
1822 case EL_YAMYAM_DOWN:
1823 case EL_DARK_YAMYAM:
1826 case EL_SP_SNIKSNAK:
1827 case EL_SP_ELECTRON:
1836 case EL_AMOEBA_FULL:
1841 case EL_AMOEBA_DROP:
1842 if (y == lev_fieldy - 1)
1844 Feld[x][y] = EL_AMOEBA_GROWING;
1845 Store[x][y] = EL_AMOEBA_WET;
1849 case EL_DYNAMITE_ACTIVE:
1850 case EL_SP_DISK_RED_ACTIVE:
1851 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1852 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1853 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1854 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1855 MovDelay[x][y] = 96;
1858 case EL_EM_DYNAMITE_ACTIVE:
1859 MovDelay[x][y] = 32;
1863 local_player->lights_still_needed++;
1867 local_player->friends_still_needed++;
1872 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1875 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1876 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1877 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1878 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1879 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1880 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1881 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1882 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1883 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1884 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1885 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1886 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1889 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1890 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1891 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1893 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1895 game.belt_dir[belt_nr] = belt_dir;
1896 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1898 else /* more than one switch -- set it like the first switch */
1900 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1905 #if !USE_BOTH_SWITCHGATE_SWITCHES
1906 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1908 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1911 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1913 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1917 case EL_LIGHT_SWITCH_ACTIVE:
1919 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1922 case EL_INVISIBLE_STEELWALL:
1923 case EL_INVISIBLE_WALL:
1924 case EL_INVISIBLE_SAND:
1925 if (game.light_time_left > 0 ||
1926 game.lenses_time_left > 0)
1927 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1930 case EL_EMC_MAGIC_BALL:
1931 if (game.ball_state)
1932 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1935 case EL_EMC_MAGIC_BALL_SWITCH:
1936 if (game.ball_state)
1937 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1941 if (IS_CUSTOM_ELEMENT(element))
1943 if (CAN_MOVE(element))
1946 #if USE_NEW_CUSTOM_VALUE
1947 if (!element_info[element].use_last_ce_value || init_game)
1948 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1951 else if (IS_GROUP_ELEMENT(element))
1953 Feld[x][y] = GetElementFromGroupElement(element);
1955 InitField(x, y, init_game);
1962 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1965 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1967 InitField(x, y, init_game);
1969 /* not needed to call InitMovDir() -- already done by InitField()! */
1970 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1971 CAN_MOVE(Feld[x][y]))
1975 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1977 int old_element = Feld[x][y];
1979 InitField(x, y, init_game);
1981 /* not needed to call InitMovDir() -- already done by InitField()! */
1982 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1983 CAN_MOVE(old_element) &&
1984 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1987 /* this case is in fact a combination of not less than three bugs:
1988 first, it calls InitMovDir() for elements that can move, although this is
1989 already done by InitField(); then, it checks the element that was at this
1990 field _before_ the call to InitField() (which can change it); lastly, it
1991 was not called for "mole with direction" elements, which were treated as
1992 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1998 static int get_key_element_from_nr(int key_nr)
2000 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2001 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2002 EL_EM_KEY_1 : EL_KEY_1);
2004 return key_base_element + key_nr;
2007 static int get_next_dropped_element(struct PlayerInfo *player)
2009 return (player->inventory_size > 0 ?
2010 player->inventory_element[player->inventory_size - 1] :
2011 player->inventory_infinite_element != EL_UNDEFINED ?
2012 player->inventory_infinite_element :
2013 player->dynabombs_left > 0 ?
2014 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2018 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2020 /* pos >= 0: get element from bottom of the stack;
2021 pos < 0: get element from top of the stack */
2025 int min_inventory_size = -pos;
2026 int inventory_pos = player->inventory_size - min_inventory_size;
2027 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2029 return (player->inventory_size >= min_inventory_size ?
2030 player->inventory_element[inventory_pos] :
2031 player->inventory_infinite_element != EL_UNDEFINED ?
2032 player->inventory_infinite_element :
2033 player->dynabombs_left >= min_dynabombs_left ?
2034 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2039 int min_dynabombs_left = pos + 1;
2040 int min_inventory_size = pos + 1 - player->dynabombs_left;
2041 int inventory_pos = pos - player->dynabombs_left;
2043 return (player->inventory_infinite_element != EL_UNDEFINED ?
2044 player->inventory_infinite_element :
2045 player->dynabombs_left >= min_dynabombs_left ?
2046 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2047 player->inventory_size >= min_inventory_size ?
2048 player->inventory_element[inventory_pos] :
2053 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2055 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2056 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2059 if (gpo1->sort_priority != gpo2->sort_priority)
2060 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2062 compare_result = gpo1->nr - gpo2->nr;
2064 return compare_result;
2067 void InitGameControlValues()
2071 for (i = 0; game_panel_controls[i].nr != -1; i++)
2073 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2074 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2075 struct TextPosInfo *pos = gpc->pos;
2077 int type = gpc->type;
2081 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2082 Error(ERR_EXIT, "this should not happen -- please debug");
2085 /* force update of game controls after initialization */
2086 gpc->value = gpc->last_value = -1;
2087 gpc->frame = gpc->last_frame = -1;
2088 gpc->gfx_frame = -1;
2090 /* determine panel value width for later calculation of alignment */
2091 if (type == TYPE_INTEGER || type == TYPE_STRING)
2093 pos->width = pos->size * getFontWidth(pos->font);
2094 pos->height = getFontHeight(pos->font);
2096 else if (type == TYPE_ELEMENT)
2098 pos->width = pos->size;
2099 pos->height = pos->size;
2102 /* fill structure for game panel draw order */
2104 gpo->sort_priority = pos->sort_priority;
2107 /* sort game panel controls according to sort_priority and control number */
2108 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2109 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2112 void UpdatePlayfieldElementCount()
2116 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2117 element_info[i].element_count = 0;
2119 SCAN_PLAYFIELD(x, y)
2121 element_info[Feld[x][y]].element_count++;
2124 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2125 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2126 if (IS_IN_GROUP(j, i))
2127 element_info[EL_GROUP_START + i].element_count +=
2128 element_info[j].element_count;
2131 void UpdateGameControlValues()
2134 int time = (local_player->LevelSolved ?
2135 local_player->LevelSolved_CountingTime :
2136 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137 level.native_em_level->lev->time :
2138 level.time == 0 ? TimePlayed : TimeLeft);
2139 int score = (local_player->LevelSolved ?
2140 local_player->LevelSolved_CountingScore :
2141 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->score :
2143 local_player->score);
2144 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145 level.native_em_level->lev->required :
2146 local_player->gems_still_needed);
2147 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148 level.native_em_level->lev->required > 0 :
2149 local_player->gems_still_needed > 0 ||
2150 local_player->sokobanfields_still_needed > 0 ||
2151 local_player->lights_still_needed > 0);
2153 UpdatePlayfieldElementCount();
2155 /* update game panel control values */
2157 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2158 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2161 for (i = 0; i < MAX_NUM_KEYS; i++)
2162 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2163 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2164 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166 if (game.centered_player_nr == -1)
2168 for (i = 0; i < MAX_PLAYERS; i++)
2170 for (k = 0; k < MAX_NUM_KEYS; k++)
2172 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2174 if (level.native_em_level->ply[i]->keys & (1 << k))
2175 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2176 get_key_element_from_nr(k);
2178 else if (stored_player[i].key[k])
2179 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2180 get_key_element_from_nr(k);
2183 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2184 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185 level.native_em_level->ply[i]->dynamite;
2187 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2188 stored_player[i].inventory_size;
2190 if (stored_player[i].num_white_keys > 0)
2191 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2194 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2195 stored_player[i].num_white_keys;
2200 int player_nr = game.centered_player_nr;
2202 for (k = 0; k < MAX_NUM_KEYS; k++)
2204 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2206 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2207 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2208 get_key_element_from_nr(k);
2210 else if (stored_player[player_nr].key[k])
2211 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2212 get_key_element_from_nr(k);
2215 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2216 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217 level.native_em_level->ply[player_nr]->dynamite;
2219 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2220 stored_player[player_nr].inventory_size;
2222 if (stored_player[player_nr].num_white_keys > 0)
2223 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2225 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2226 stored_player[player_nr].num_white_keys;
2229 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2231 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2232 get_inventory_element_from_pos(local_player, i);
2233 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2234 get_inventory_element_from_pos(local_player, -i - 1);
2237 game_panel_controls[GAME_PANEL_SCORE].value = score;
2239 game_panel_controls[GAME_PANEL_TIME].value = time;
2241 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2242 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2243 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2245 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2246 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2248 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2249 local_player->shield_normal_time_left;
2250 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2251 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2253 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2254 local_player->shield_deadly_time_left;
2256 game_panel_controls[GAME_PANEL_EXIT].value =
2257 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2259 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2260 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2261 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2262 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2263 EL_EMC_MAGIC_BALL_SWITCH);
2265 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2266 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2267 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2268 game.light_time_left;
2270 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2271 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2272 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2273 game.timegate_time_left;
2275 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2276 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2278 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2279 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2280 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2281 game.lenses_time_left;
2283 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2284 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2285 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2286 game.magnify_time_left;
2288 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2289 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2290 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2291 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2292 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2293 EL_BALLOON_SWITCH_NONE);
2295 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2296 local_player->dynabomb_count;
2297 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2298 local_player->dynabomb_size;
2299 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2300 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2302 game_panel_controls[GAME_PANEL_PENGUINS].value =
2303 local_player->friends_still_needed;
2305 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2306 local_player->sokobanfields_still_needed;
2307 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2308 local_player->sokobanfields_still_needed;
2310 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2311 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2313 for (i = 0; i < NUM_BELTS; i++)
2315 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2316 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2317 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2318 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2319 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2322 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2323 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2324 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2325 game.magic_wall_time_left;
2327 #if USE_PLAYER_GRAVITY
2328 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2329 local_player->gravity;
2331 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2334 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2335 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2337 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2338 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2339 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2340 game.panel.element[i].id : EL_UNDEFINED);
2342 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2343 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2344 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2345 element_info[game.panel.element_count[i].id].element_count :
2348 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2349 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2350 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2351 element_info[game.panel.ce_score[i].id].collect_score : 0);
2353 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2354 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2355 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2356 element_info[game.panel.ce_score_element[i].id].collect_score :
2359 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2360 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2361 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2363 /* update game panel control frames */
2365 for (i = 0; game_panel_controls[i].nr != -1; i++)
2367 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2369 if (gpc->type == TYPE_ELEMENT)
2371 int last_anim_random_frame = gfx.anim_random_frame;
2372 int element = gpc->value;
2373 int graphic = el2panelimg(element);
2375 if (gpc->value != gpc->last_value)
2378 gpc->gfx_random = INIT_GFX_RANDOM();
2384 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2385 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2386 gpc->gfx_random = INIT_GFX_RANDOM();
2389 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2390 gfx.anim_random_frame = gpc->gfx_random;
2392 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2393 gpc->gfx_frame = element_info[element].collect_score;
2395 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2398 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2399 gfx.anim_random_frame = last_anim_random_frame;
2404 void DisplayGameControlValues()
2406 boolean redraw_panel = FALSE;
2409 for (i = 0; game_panel_controls[i].nr != -1; i++)
2411 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2413 if (PANEL_DEACTIVATED(gpc->pos))
2416 if (gpc->value == gpc->last_value &&
2417 gpc->frame == gpc->last_frame)
2420 redraw_panel = TRUE;
2426 /* copy default game door content to main double buffer */
2427 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2428 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2430 /* redraw game control buttons */
2432 RedrawGameButtons();
2438 game_status = GAME_MODE_PSEUDO_PANEL;
2441 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2443 for (i = 0; game_panel_controls[i].nr != -1; i++)
2447 int nr = game_panel_order[i].nr;
2448 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2450 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2453 struct TextPosInfo *pos = gpc->pos;
2454 int type = gpc->type;
2455 int value = gpc->value;
2456 int frame = gpc->frame;
2458 int last_value = gpc->last_value;
2459 int last_frame = gpc->last_frame;
2461 int size = pos->size;
2462 int font = pos->font;
2463 boolean draw_masked = pos->draw_masked;
2464 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2466 if (PANEL_DEACTIVATED(pos))
2470 if (value == last_value && frame == last_frame)
2474 gpc->last_value = value;
2475 gpc->last_frame = frame;
2478 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2481 if (type == TYPE_INTEGER)
2483 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2484 nr == GAME_PANEL_TIME)
2486 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2488 if (use_dynamic_size) /* use dynamic number of digits */
2490 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2491 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2492 int size2 = size1 + 1;
2493 int font1 = pos->font;
2494 int font2 = pos->font_alt;
2496 size = (value < value_change ? size1 : size2);
2497 font = (value < value_change ? font1 : font2);
2500 /* clear background if value just changed its size (dynamic digits) */
2501 if ((last_value < value_change) != (value < value_change))
2503 int width1 = size1 * getFontWidth(font1);
2504 int width2 = size2 * getFontWidth(font2);
2505 int max_width = MAX(width1, width2);
2506 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2508 pos->width = max_width;
2510 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2511 max_width, max_height);
2518 /* correct text size if "digits" is zero or less */
2520 size = strlen(int2str(value, size));
2522 /* dynamically correct text alignment */
2523 pos->width = size * getFontWidth(font);
2526 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2527 int2str(value, size), font, mask_mode);
2529 else if (type == TYPE_ELEMENT)
2531 int element, graphic;
2535 int dst_x = PANEL_XPOS(pos);
2536 int dst_y = PANEL_YPOS(pos);
2539 if (value != EL_UNDEFINED && value != EL_EMPTY)
2542 graphic = el2panelimg(value);
2544 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2547 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2551 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2554 width = graphic_info[graphic].width * size / TILESIZE;
2555 height = graphic_info[graphic].height * size / TILESIZE;
2559 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2560 dst_x - src_x, dst_y - src_y);
2561 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2566 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2571 if (value == EL_UNDEFINED || value == EL_EMPTY)
2573 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2574 graphic = el2panelimg(element);
2576 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2577 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2578 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2583 graphic = el2panelimg(value);
2585 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2588 width = graphic_info[graphic].width * size / TILESIZE;
2589 height = graphic_info[graphic].height * size / TILESIZE;
2591 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2594 else if (type == TYPE_STRING)
2596 boolean active = (value != 0);
2597 char *state_normal = "off";
2598 char *state_active = "on";
2599 char *state = (active ? state_active : state_normal);
2600 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2601 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2602 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2603 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2605 if (nr == GAME_PANEL_GRAVITY_STATE)
2607 int font1 = pos->font; /* (used for normal state) */
2608 int font2 = pos->font_alt; /* (used for active state) */
2610 int size1 = strlen(state_normal);
2611 int size2 = strlen(state_active);
2612 int width1 = size1 * getFontWidth(font1);
2613 int width2 = size2 * getFontWidth(font2);
2614 int max_width = MAX(width1, width2);
2615 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2617 pos->width = max_width;
2619 /* clear background for values that may have changed its size */
2620 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2621 max_width, max_height);
2624 font = (active ? font2 : font1);
2634 /* don't truncate output if "chars" is zero or less */
2637 /* dynamically correct text alignment */
2638 pos->width = size * getFontWidth(font);
2642 s_cut = getStringCopyN(s, size);
2644 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2645 s_cut, font, mask_mode);
2651 redraw_mask |= REDRAW_DOOR_1;
2654 game_status = GAME_MODE_PLAYING;
2657 void DrawGameValue_Emeralds(int value)
2659 struct TextPosInfo *pos = &game.panel.gems;
2661 int font_nr = pos->font;
2663 int font_nr = FONT_TEXT_2;
2665 int font_width = getFontWidth(font_nr);
2666 int chars = pos->size;
2669 return; /* !!! USE NEW STUFF !!! */
2672 if (PANEL_DEACTIVATED(pos))
2675 pos->width = chars * font_width;
2677 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2680 void DrawGameValue_Dynamite(int value)
2682 struct TextPosInfo *pos = &game.panel.inventory_count;
2684 int font_nr = pos->font;
2686 int font_nr = FONT_TEXT_2;
2688 int font_width = getFontWidth(font_nr);
2689 int chars = pos->size;
2692 return; /* !!! USE NEW STUFF !!! */
2695 if (PANEL_DEACTIVATED(pos))
2698 pos->width = chars * font_width;
2700 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2703 void DrawGameValue_Score(int value)
2705 struct TextPosInfo *pos = &game.panel.score;
2707 int font_nr = pos->font;
2709 int font_nr = FONT_TEXT_2;
2711 int font_width = getFontWidth(font_nr);
2712 int chars = pos->size;
2715 return; /* !!! USE NEW STUFF !!! */
2718 if (PANEL_DEACTIVATED(pos))
2721 pos->width = chars * font_width;
2723 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2726 void DrawGameValue_Time(int value)
2728 struct TextPosInfo *pos = &game.panel.time;
2729 static int last_value = -1;
2732 int chars = pos->size;
2734 int font1_nr = pos->font;
2735 int font2_nr = pos->font_alt;
2737 int font1_nr = FONT_TEXT_2;
2738 int font2_nr = FONT_TEXT_1;
2740 int font_nr = font1_nr;
2741 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2744 return; /* !!! USE NEW STUFF !!! */
2747 if (PANEL_DEACTIVATED(pos))
2750 if (use_dynamic_chars) /* use dynamic number of chars */
2752 chars = (value < 1000 ? chars1 : chars2);
2753 font_nr = (value < 1000 ? font1_nr : font2_nr);
2756 /* clear background if value just changed its size (dynamic chars only) */
2757 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2759 int width1 = chars1 * getFontWidth(font1_nr);
2760 int width2 = chars2 * getFontWidth(font2_nr);
2761 int max_width = MAX(width1, width2);
2762 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2764 pos->width = max_width;
2766 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2767 max_width, max_height);
2770 pos->width = chars * getFontWidth(font_nr);
2772 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2777 void DrawGameValue_Level(int value)
2779 struct TextPosInfo *pos = &game.panel.level_number;
2782 int chars = pos->size;
2784 int font1_nr = pos->font;
2785 int font2_nr = pos->font_alt;
2787 int font1_nr = FONT_TEXT_2;
2788 int font2_nr = FONT_TEXT_1;
2790 int font_nr = font1_nr;
2791 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2794 return; /* !!! USE NEW STUFF !!! */
2797 if (PANEL_DEACTIVATED(pos))
2800 if (use_dynamic_chars) /* use dynamic number of chars */
2802 chars = (level_nr < 100 ? chars1 : chars2);
2803 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2806 pos->width = chars * getFontWidth(font_nr);
2808 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2811 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2814 struct TextPosInfo *pos = &game.panel.keys;
2817 int base_key_graphic = EL_KEY_1;
2822 return; /* !!! USE NEW STUFF !!! */
2826 if (PANEL_DEACTIVATED(pos))
2831 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2832 base_key_graphic = EL_EM_KEY_1;
2836 pos->width = 4 * MINI_TILEX;
2840 for (i = 0; i < MAX_NUM_KEYS; i++)
2842 /* currently only 4 of 8 possible keys are displayed */
2843 for (i = 0; i < STD_NUM_KEYS; i++)
2847 struct TextPosInfo *pos = &game.panel.key[i];
2849 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2850 int src_y = DOOR_GFX_PAGEY1 + 123;
2852 int dst_x = PANEL_XPOS(pos);
2853 int dst_y = PANEL_YPOS(pos);
2855 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2856 int dst_y = PANEL_YPOS(pos);
2860 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2861 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2863 int graphic = el2edimg(element);
2867 if (PANEL_DEACTIVATED(pos))
2872 /* masked blit with tiles from half-size scaled bitmap does not work yet
2873 (no mask bitmap created for these sizes after loading and scaling) --
2874 solution: load without creating mask, scale, then create final mask */
2876 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2877 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2882 int graphic = el2edimg(base_key_graphic + i);
2887 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2889 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2890 dst_x - src_x, dst_y - src_y);
2891 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2897 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2899 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2900 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2903 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2905 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2906 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2914 void DrawGameValue_Emeralds(int value)
2916 int font_nr = FONT_TEXT_2;
2917 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2919 if (PANEL_DEACTIVATED(game.panel.gems))
2922 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2925 void DrawGameValue_Dynamite(int value)
2927 int font_nr = FONT_TEXT_2;
2928 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2930 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2933 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2936 void DrawGameValue_Score(int value)
2938 int font_nr = FONT_TEXT_2;
2939 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2941 if (PANEL_DEACTIVATED(game.panel.score))
2944 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2947 void DrawGameValue_Time(int value)
2949 int font1_nr = FONT_TEXT_2;
2951 int font2_nr = FONT_TEXT_1;
2953 int font2_nr = FONT_LEVEL_NUMBER;
2955 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2956 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2958 if (PANEL_DEACTIVATED(game.panel.time))
2961 /* clear background if value just changed its size */
2962 if (value == 999 || value == 1000)
2963 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2966 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2968 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2971 void DrawGameValue_Level(int value)
2973 int font1_nr = FONT_TEXT_2;
2975 int font2_nr = FONT_TEXT_1;
2977 int font2_nr = FONT_LEVEL_NUMBER;
2980 if (PANEL_DEACTIVATED(game.panel.level))
2984 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2986 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2989 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2991 int base_key_graphic = EL_KEY_1;
2994 if (PANEL_DEACTIVATED(game.panel.keys))
2997 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2998 base_key_graphic = EL_EM_KEY_1;
3000 /* currently only 4 of 8 possible keys are displayed */
3001 for (i = 0; i < STD_NUM_KEYS; i++)
3003 int x = XX_KEYS + i * MINI_TILEX;
3007 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3009 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3010 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3016 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3019 int key[MAX_NUM_KEYS];
3022 /* prevent EM engine from updating time/score values parallel to GameWon() */
3023 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3024 local_player->LevelSolved)
3027 for (i = 0; i < MAX_NUM_KEYS; i++)
3028 key[i] = key_bits & (1 << i);
3030 DrawGameValue_Level(level_nr);
3032 DrawGameValue_Emeralds(emeralds);
3033 DrawGameValue_Dynamite(dynamite);
3034 DrawGameValue_Score(score);
3035 DrawGameValue_Time(time);
3037 DrawGameValue_Keys(key);
3040 void UpdateGameDoorValues()
3042 UpdateGameControlValues();
3045 void DrawGameDoorValues()
3047 DisplayGameControlValues();
3050 void DrawGameDoorValues_OLD()
3052 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3053 int dynamite_value = 0;
3054 int score_value = (local_player->LevelSolved ? local_player->score_final :
3055 local_player->score);
3056 int gems_value = local_player->gems_still_needed;
3060 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3062 DrawGameDoorValues_EM();
3067 if (game.centered_player_nr == -1)
3069 for (i = 0; i < MAX_PLAYERS; i++)
3071 for (j = 0; j < MAX_NUM_KEYS; j++)
3072 if (stored_player[i].key[j])
3073 key_bits |= (1 << j);
3075 dynamite_value += stored_player[i].inventory_size;
3080 int player_nr = game.centered_player_nr;
3082 for (i = 0; i < MAX_NUM_KEYS; i++)
3083 if (stored_player[player_nr].key[i])
3084 key_bits |= (1 << i);
3086 dynamite_value = stored_player[player_nr].inventory_size;
3089 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3095 =============================================================================
3097 -----------------------------------------------------------------------------
3098 initialize game engine due to level / tape version number
3099 =============================================================================
3102 static void InitGameEngine()
3104 int i, j, k, l, x, y;
3106 /* set game engine from tape file when re-playing, else from level file */
3107 game.engine_version = (tape.playing ? tape.engine_version :
3108 level.game_version);
3110 /* ---------------------------------------------------------------------- */
3111 /* set flags for bugs and changes according to active game engine version */
3112 /* ---------------------------------------------------------------------- */
3115 Summary of bugfix/change:
3116 Fixed handling for custom elements that change when pushed by the player.
3118 Fixed/changed in version:
3122 Before 3.1.0, custom elements that "change when pushing" changed directly
3123 after the player started pushing them (until then handled in "DigField()").
3124 Since 3.1.0, these custom elements are not changed until the "pushing"
3125 move of the element is finished (now handled in "ContinueMoving()").
3127 Affected levels/tapes:
3128 The first condition is generally needed for all levels/tapes before version
3129 3.1.0, which might use the old behaviour before it was changed; known tapes
3130 that are affected are some tapes from the level set "Walpurgis Gardens" by
3132 The second condition is an exception from the above case and is needed for
3133 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3134 above (including some development versions of 3.1.0), but before it was
3135 known that this change would break tapes like the above and was fixed in
3136 3.1.1, so that the changed behaviour was active although the engine version
3137 while recording maybe was before 3.1.0. There is at least one tape that is
3138 affected by this exception, which is the tape for the one-level set "Bug
3139 Machine" by Juergen Bonhagen.
3142 game.use_change_when_pushing_bug =
3143 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3145 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3146 tape.game_version < VERSION_IDENT(3,1,1,0)));
3149 Summary of bugfix/change:
3150 Fixed handling for blocking the field the player leaves when moving.
3152 Fixed/changed in version:
3156 Before 3.1.1, when "block last field when moving" was enabled, the field
3157 the player is leaving when moving was blocked for the time of the move,
3158 and was directly unblocked afterwards. This resulted in the last field
3159 being blocked for exactly one less than the number of frames of one player
3160 move. Additionally, even when blocking was disabled, the last field was
3161 blocked for exactly one frame.
3162 Since 3.1.1, due to changes in player movement handling, the last field
3163 is not blocked at all when blocking is disabled. When blocking is enabled,
3164 the last field is blocked for exactly the number of frames of one player
3165 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3166 last field is blocked for exactly one more than the number of frames of
3169 Affected levels/tapes:
3170 (!!! yet to be determined -- probably many !!!)
3173 game.use_block_last_field_bug =
3174 (game.engine_version < VERSION_IDENT(3,1,1,0));
3177 Summary of bugfix/change:
3178 Changed behaviour of CE changes with multiple changes per single frame.
3180 Fixed/changed in version:
3184 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3185 This resulted in race conditions where CEs seem to behave strange in some
3186 situations (where triggered CE changes were just skipped because there was
3187 already a CE change on that tile in the playfield in that engine frame).
3188 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3189 (The number of changes per frame must be limited in any case, because else
3190 it is easily possible to define CE changes that would result in an infinite
3191 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3192 should be set large enough so that it would only be reached in cases where
3193 the corresponding CE change conditions run into a loop. Therefore, it seems
3194 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3195 maximal number of change pages for custom elements.)
3197 Affected levels/tapes:
3201 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3202 game.max_num_changes_per_frame = 1;
3204 game.max_num_changes_per_frame =
3205 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3208 /* ---------------------------------------------------------------------- */
3210 /* default scan direction: scan playfield from top/left to bottom/right */
3211 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3213 /* dynamically adjust element properties according to game engine version */
3214 InitElementPropertiesEngine(game.engine_version);
3217 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3218 printf(" tape version == %06d [%s] [file: %06d]\n",
3219 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3221 printf(" => game.engine_version == %06d\n", game.engine_version);
3224 /* ---------- initialize player's initial move delay --------------------- */
3226 /* dynamically adjust player properties according to level information */
3227 for (i = 0; i < MAX_PLAYERS; i++)
3228 game.initial_move_delay_value[i] =
3229 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3231 /* dynamically adjust player properties according to game engine version */
3232 for (i = 0; i < MAX_PLAYERS; i++)
3233 game.initial_move_delay[i] =
3234 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3235 game.initial_move_delay_value[i] : 0);
3237 /* ---------- initialize player's initial push delay --------------------- */
3239 /* dynamically adjust player properties according to game engine version */
3240 game.initial_push_delay_value =
3241 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3243 /* ---------- initialize changing elements ------------------------------- */
3245 /* initialize changing elements information */
3246 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248 struct ElementInfo *ei = &element_info[i];
3250 /* this pointer might have been changed in the level editor */
3251 ei->change = &ei->change_page[0];
3253 if (!IS_CUSTOM_ELEMENT(i))
3255 ei->change->target_element = EL_EMPTY_SPACE;
3256 ei->change->delay_fixed = 0;
3257 ei->change->delay_random = 0;
3258 ei->change->delay_frames = 1;
3261 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3263 ei->has_change_event[j] = FALSE;
3265 ei->event_page_nr[j] = 0;
3266 ei->event_page[j] = &ei->change_page[0];
3270 /* add changing elements from pre-defined list */
3271 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3273 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3274 struct ElementInfo *ei = &element_info[ch_delay->element];
3276 ei->change->target_element = ch_delay->target_element;
3277 ei->change->delay_fixed = ch_delay->change_delay;
3279 ei->change->pre_change_function = ch_delay->pre_change_function;
3280 ei->change->change_function = ch_delay->change_function;
3281 ei->change->post_change_function = ch_delay->post_change_function;
3283 ei->change->can_change = TRUE;
3284 ei->change->can_change_or_has_action = TRUE;
3286 ei->has_change_event[CE_DELAY] = TRUE;
3288 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3289 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3292 /* ---------- initialize internal run-time variables ------------- */
3294 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3296 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3298 for (j = 0; j < ei->num_change_pages; j++)
3300 ei->change_page[j].can_change_or_has_action =
3301 (ei->change_page[j].can_change |
3302 ei->change_page[j].has_action);
3306 /* add change events from custom element configuration */
3307 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3309 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3311 for (j = 0; j < ei->num_change_pages; j++)
3313 if (!ei->change_page[j].can_change_or_has_action)
3316 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3318 /* only add event page for the first page found with this event */
3319 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3321 ei->has_change_event[k] = TRUE;
3323 ei->event_page_nr[k] = j;
3324 ei->event_page[k] = &ei->change_page[j];
3330 /* ---------- initialize run-time trigger player and element ------------- */
3332 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336 for (j = 0; j < ei->num_change_pages; j++)
3338 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3339 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3340 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_1;
3341 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3342 ei->change_page[j].actual_trigger_ce_value = 0;
3343 ei->change_page[j].actual_trigger_ce_score = 0;
3347 /* ---------- initialize trigger events ---------------------------------- */
3349 /* initialize trigger events information */
3350 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3351 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3352 trigger_events[i][j] = FALSE;
3354 /* add trigger events from element change event properties */
3355 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3357 struct ElementInfo *ei = &element_info[i];
3359 for (j = 0; j < ei->num_change_pages; j++)
3361 if (!ei->change_page[j].can_change_or_has_action)
3364 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3366 int trigger_element = ei->change_page[j].trigger_element;
3368 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3370 if (ei->change_page[j].has_event[k])
3372 if (IS_GROUP_ELEMENT(trigger_element))
3374 struct ElementGroupInfo *group =
3375 element_info[trigger_element].group;
3377 for (l = 0; l < group->num_elements_resolved; l++)
3378 trigger_events[group->element_resolved[l]][k] = TRUE;
3380 else if (trigger_element == EL_ANY_ELEMENT)
3381 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3382 trigger_events[l][k] = TRUE;
3384 trigger_events[trigger_element][k] = TRUE;
3391 /* ---------- initialize push delay -------------------------------------- */
3393 /* initialize push delay values to default */
3394 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3396 if (!IS_CUSTOM_ELEMENT(i))
3398 /* set default push delay values (corrected since version 3.0.7-1) */
3399 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3401 element_info[i].push_delay_fixed = 2;
3402 element_info[i].push_delay_random = 8;
3406 element_info[i].push_delay_fixed = 8;
3407 element_info[i].push_delay_random = 8;
3412 /* set push delay value for certain elements from pre-defined list */
3413 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3415 int e = push_delay_list[i].element;
3417 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3418 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3421 /* set push delay value for Supaplex elements for newer engine versions */
3422 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3424 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3426 if (IS_SP_ELEMENT(i))
3428 /* set SP push delay to just enough to push under a falling zonk */
3429 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3431 element_info[i].push_delay_fixed = delay;
3432 element_info[i].push_delay_random = 0;
3437 /* ---------- initialize move stepsize ----------------------------------- */
3439 /* initialize move stepsize values to default */
3440 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3441 if (!IS_CUSTOM_ELEMENT(i))
3442 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3444 /* set move stepsize value for certain elements from pre-defined list */
3445 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3447 int e = move_stepsize_list[i].element;
3449 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3452 /* ---------- initialize collect score ----------------------------------- */
3454 /* initialize collect score values for custom elements from initial value */
3455 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3456 if (IS_CUSTOM_ELEMENT(i))
3457 element_info[i].collect_score = element_info[i].collect_score_initial;
3459 /* ---------- initialize collect count ----------------------------------- */
3461 /* initialize collect count values for non-custom elements */
3462 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3463 if (!IS_CUSTOM_ELEMENT(i))
3464 element_info[i].collect_count_initial = 0;
3466 /* add collect count values for all elements from pre-defined list */
3467 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3468 element_info[collect_count_list[i].element].collect_count_initial =
3469 collect_count_list[i].count;
3471 /* ---------- initialize access direction -------------------------------- */
3473 /* initialize access direction values to default (access from every side) */
3474 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3475 if (!IS_CUSTOM_ELEMENT(i))
3476 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3478 /* set access direction value for certain elements from pre-defined list */
3479 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3480 element_info[access_direction_list[i].element].access_direction =
3481 access_direction_list[i].direction;
3483 /* ---------- initialize explosion content ------------------------------- */
3484 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3486 if (IS_CUSTOM_ELEMENT(i))
3489 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3491 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3493 element_info[i].content.e[x][y] =
3494 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3495 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3496 i == EL_PLAYER_3 ? EL_EMERALD :
3497 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3498 i == EL_MOLE ? EL_EMERALD_RED :
3499 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3500 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3501 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3502 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3503 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3504 i == EL_WALL_EMERALD ? EL_EMERALD :
3505 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3506 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3507 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3508 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3509 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3510 i == EL_WALL_PEARL ? EL_PEARL :
3511 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3516 /* ---------- initialize recursion detection ------------------------------ */
3517 recursion_loop_depth = 0;
3518 recursion_loop_detected = FALSE;
3519 recursion_loop_element = EL_UNDEFINED;
3521 /* ---------- initialize graphics engine ---------------------------------- */
3522 game.scroll_delay_value =
3523 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3524 setup.scroll_delay ? setup.scroll_delay_value : 0);
3525 game.scroll_delay_value =
3526 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3529 int get_num_special_action(int element, int action_first, int action_last)
3531 int num_special_action = 0;
3534 for (i = action_first; i <= action_last; i++)
3536 boolean found = FALSE;
3538 for (j = 0; j < NUM_DIRECTIONS; j++)
3539 if (el_act_dir2img(element, i, j) !=
3540 el_act_dir2img(element, ACTION_DEFAULT, j))
3544 num_special_action++;
3549 return num_special_action;
3554 =============================================================================
3556 -----------------------------------------------------------------------------
3557 initialize and start new game
3558 =============================================================================
3563 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3564 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3565 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3567 boolean do_fading = (game_status == GAME_MODE_MAIN);
3571 game_status = GAME_MODE_PLAYING;
3574 InitGameControlValues();
3576 /* don't play tapes over network */
3577 network_playing = (options.network && !tape.playing);
3579 for (i = 0; i < MAX_PLAYERS; i++)
3581 struct PlayerInfo *player = &stored_player[i];
3583 player->index_nr = i;
3584 player->index_bit = (1 << i);
3585 player->element_nr = EL_PLAYER_1 + i;
3587 player->present = FALSE;
3588 player->active = FALSE;
3589 player->killed = FALSE;
3592 player->effective_action = 0;
3593 player->programmed_action = 0;
3596 player->score_final = 0;
3598 player->gems_still_needed = level.gems_needed;
3599 player->sokobanfields_still_needed = 0;
3600 player->lights_still_needed = 0;
3601 player->friends_still_needed = 0;
3603 for (j = 0; j < MAX_NUM_KEYS; j++)
3604 player->key[j] = FALSE;
3606 player->num_white_keys = 0;
3608 player->dynabomb_count = 0;
3609 player->dynabomb_size = 1;
3610 player->dynabombs_left = 0;
3611 player->dynabomb_xl = FALSE;
3613 player->MovDir = MV_NONE;
3616 player->GfxDir = MV_NONE;
3617 player->GfxAction = ACTION_DEFAULT;
3619 player->StepFrame = 0;
3621 player->use_murphy = FALSE;
3622 player->artwork_element =
3623 (level.use_artwork_element[i] ? level.artwork_element[i] :
3624 player->element_nr);
3626 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3627 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3629 player->gravity = level.initial_player_gravity[i];
3631 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3633 player->actual_frame_counter = 0;
3635 player->step_counter = 0;
3637 player->last_move_dir = MV_NONE;
3639 player->is_active = FALSE;
3641 player->is_waiting = FALSE;
3642 player->is_moving = FALSE;
3643 player->is_auto_moving = FALSE;
3644 player->is_digging = FALSE;
3645 player->is_snapping = FALSE;
3646 player->is_collecting = FALSE;
3647 player->is_pushing = FALSE;
3648 player->is_switching = FALSE;
3649 player->is_dropping = FALSE;
3650 player->is_dropping_pressed = FALSE;
3652 player->is_bored = FALSE;
3653 player->is_sleeping = FALSE;
3655 player->frame_counter_bored = -1;
3656 player->frame_counter_sleeping = -1;
3658 player->anim_delay_counter = 0;
3659 player->post_delay_counter = 0;
3661 player->dir_waiting = MV_NONE;
3662 player->action_waiting = ACTION_DEFAULT;
3663 player->last_action_waiting = ACTION_DEFAULT;
3664 player->special_action_bored = ACTION_DEFAULT;
3665 player->special_action_sleeping = ACTION_DEFAULT;
3667 player->switch_x = -1;
3668 player->switch_y = -1;
3670 player->drop_x = -1;
3671 player->drop_y = -1;
3673 player->show_envelope = 0;
3675 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3677 player->push_delay = -1; /* initialized when pushing starts */
3678 player->push_delay_value = game.initial_push_delay_value;
3680 player->drop_delay = 0;
3681 player->drop_pressed_delay = 0;
3683 player->last_jx = -1;
3684 player->last_jy = -1;
3688 player->shield_normal_time_left = 0;
3689 player->shield_deadly_time_left = 0;
3691 player->inventory_infinite_element = EL_UNDEFINED;
3692 player->inventory_size = 0;
3694 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3695 SnapField(player, 0, 0);
3697 player->LevelSolved = FALSE;
3698 player->GameOver = FALSE;
3700 player->LevelSolved_GameWon = FALSE;
3701 player->LevelSolved_GameEnd = FALSE;
3702 player->LevelSolved_PanelOff = FALSE;
3703 player->LevelSolved_SaveTape = FALSE;
3704 player->LevelSolved_SaveScore = FALSE;
3705 player->LevelSolved_CountingTime = 0;
3706 player->LevelSolved_CountingScore = 0;
3709 network_player_action_received = FALSE;
3711 #if defined(NETWORK_AVALIABLE)
3712 /* initial null action */
3713 if (network_playing)
3714 SendToServer_MovePlayer(MV_NONE);
3723 TimeLeft = level.time;
3726 ScreenMovDir = MV_NONE;
3730 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3732 AllPlayersGone = FALSE;
3734 game.yamyam_content_nr = 0;
3735 game.robot_wheel_active = FALSE;
3736 game.magic_wall_active = FALSE;
3737 game.magic_wall_time_left = 0;
3738 game.light_time_left = 0;
3739 game.timegate_time_left = 0;
3740 game.switchgate_pos = 0;
3741 game.wind_direction = level.wind_direction_initial;
3743 #if !USE_PLAYER_GRAVITY
3744 game.gravity = FALSE;
3745 game.explosions_delayed = TRUE;
3748 game.lenses_time_left = 0;
3749 game.magnify_time_left = 0;
3751 game.ball_state = level.ball_state_initial;
3752 game.ball_content_nr = 0;
3754 game.envelope_active = FALSE;
3756 /* set focus to local player for network games, else to all players */
3757 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3758 game.centered_player_nr_next = game.centered_player_nr;
3759 game.set_centered_player = FALSE;
3761 if (network_playing && tape.recording)
3763 /* store client dependent player focus when recording network games */
3764 tape.centered_player_nr_next = game.centered_player_nr_next;
3765 tape.set_centered_player = TRUE;
3768 for (i = 0; i < NUM_BELTS; i++)
3770 game.belt_dir[i] = MV_NONE;
3771 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3774 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3775 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3777 SCAN_PLAYFIELD(x, y)
3779 Feld[x][y] = level.field[x][y];
3780 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3781 ChangeDelay[x][y] = 0;
3782 ChangePage[x][y] = -1;
3783 #if USE_NEW_CUSTOM_VALUE
3784 CustomValue[x][y] = 0; /* initialized in InitField() */
3786 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3788 WasJustMoving[x][y] = 0;
3789 WasJustFalling[x][y] = 0;
3790 CheckCollision[x][y] = 0;
3791 CheckImpact[x][y] = 0;
3793 Pushed[x][y] = FALSE;
3795 ChangeCount[x][y] = 0;
3796 ChangeEvent[x][y] = -1;
3798 ExplodePhase[x][y] = 0;
3799 ExplodeDelay[x][y] = 0;
3800 ExplodeField[x][y] = EX_TYPE_NONE;
3802 RunnerVisit[x][y] = 0;
3803 PlayerVisit[x][y] = 0;
3806 GfxRandom[x][y] = INIT_GFX_RANDOM();
3807 GfxElement[x][y] = EL_UNDEFINED;
3808 GfxAction[x][y] = ACTION_DEFAULT;
3809 GfxDir[x][y] = MV_NONE;
3812 SCAN_PLAYFIELD(x, y)
3814 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3816 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3818 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3821 InitField(x, y, TRUE);
3823 ResetGfxAnimation(x, y);
3828 for (i = 0; i < MAX_PLAYERS; i++)
3830 struct PlayerInfo *player = &stored_player[i];
3832 /* set number of special actions for bored and sleeping animation */
3833 player->num_special_action_bored =
3834 get_num_special_action(player->artwork_element,
3835 ACTION_BORING_1, ACTION_BORING_LAST);
3836 player->num_special_action_sleeping =
3837 get_num_special_action(player->artwork_element,
3838 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3841 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3842 emulate_sb ? EMU_SOKOBAN :
3843 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3845 #if USE_NEW_ALL_SLIPPERY
3846 /* initialize type of slippery elements */
3847 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3849 if (!IS_CUSTOM_ELEMENT(i))
3851 /* default: elements slip down either to the left or right randomly */
3852 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3854 /* SP style elements prefer to slip down on the left side */
3855 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3856 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3858 /* BD style elements prefer to slip down on the left side */
3859 if (game.emulation == EMU_BOULDERDASH)
3860 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3865 /* initialize explosion and ignition delay */
3866 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3868 if (!IS_CUSTOM_ELEMENT(i))
3871 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3872 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3873 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3874 int last_phase = (num_phase + 1) * delay;
3875 int half_phase = (num_phase / 2) * delay;
3877 element_info[i].explosion_delay = last_phase - 1;
3878 element_info[i].ignition_delay = half_phase;
3880 if (i == EL_BLACK_ORB)
3881 element_info[i].ignition_delay = 1;
3885 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3886 element_info[i].explosion_delay = 1;
3888 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3889 element_info[i].ignition_delay = 1;
3893 /* correct non-moving belts to start moving left */
3894 for (i = 0; i < NUM_BELTS; i++)
3895 if (game.belt_dir[i] == MV_NONE)
3896 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3898 /* check if any connected player was not found in playfield */
3899 for (i = 0; i < MAX_PLAYERS; i++)
3901 struct PlayerInfo *player = &stored_player[i];
3903 if (player->connected && !player->present)
3905 for (j = 0; j < MAX_PLAYERS; j++)
3907 struct PlayerInfo *some_player = &stored_player[j];
3908 int jx = some_player->jx, jy = some_player->jy;
3910 /* assign first free player found that is present in the playfield */
3911 if (some_player->present && !some_player->connected)
3913 player->present = TRUE;
3914 player->active = TRUE;
3916 some_player->present = FALSE;
3917 some_player->active = FALSE;
3919 player->artwork_element = some_player->artwork_element;
3921 player->block_last_field = some_player->block_last_field;
3922 player->block_delay_adjustment = some_player->block_delay_adjustment;
3924 StorePlayer[jx][jy] = player->element_nr;
3925 player->jx = player->last_jx = jx;
3926 player->jy = player->last_jy = jy;
3936 /* when playing a tape, eliminate all players who do not participate */
3938 for (i = 0; i < MAX_PLAYERS; i++)
3940 if (stored_player[i].active && !tape.player_participates[i])
3942 struct PlayerInfo *player = &stored_player[i];
3943 int jx = player->jx, jy = player->jy;
3945 player->active = FALSE;
3946 StorePlayer[jx][jy] = 0;
3947 Feld[jx][jy] = EL_EMPTY;
3951 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3953 /* when in single player mode, eliminate all but the first active player */
3955 for (i = 0; i < MAX_PLAYERS; i++)
3957 if (stored_player[i].active)
3959 for (j = i + 1; j < MAX_PLAYERS; j++)
3961 if (stored_player[j].active)
3963 struct PlayerInfo *player = &stored_player[j];
3964 int jx = player->jx, jy = player->jy;
3966 player->active = FALSE;
3967 player->present = FALSE;
3969 StorePlayer[jx][jy] = 0;
3970 Feld[jx][jy] = EL_EMPTY;
3977 /* when recording the game, store which players take part in the game */
3980 for (i = 0; i < MAX_PLAYERS; i++)
3981 if (stored_player[i].active)
3982 tape.player_participates[i] = TRUE;
3987 for (i = 0; i < MAX_PLAYERS; i++)
3989 struct PlayerInfo *player = &stored_player[i];
3991 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3996 if (local_player == player)
3997 printf("Player %d is local player.\n", i+1);
4001 if (BorderElement == EL_EMPTY)
4004 SBX_Right = lev_fieldx - SCR_FIELDX;
4006 SBY_Lower = lev_fieldy - SCR_FIELDY;
4011 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4013 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4016 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4017 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4019 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4020 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4022 /* if local player not found, look for custom element that might create
4023 the player (make some assumptions about the right custom element) */
4024 if (!local_player->present)
4026 int start_x = 0, start_y = 0;
4027 int found_rating = 0;
4028 int found_element = EL_UNDEFINED;
4029 int player_nr = local_player->index_nr;
4031 SCAN_PLAYFIELD(x, y)
4033 int element = Feld[x][y];
4038 if (level.use_start_element[player_nr] &&
4039 level.start_element[player_nr] == element &&
4046 found_element = element;
4049 if (!IS_CUSTOM_ELEMENT(element))
4052 if (CAN_CHANGE(element))
4054 for (i = 0; i < element_info[element].num_change_pages; i++)
4056 /* check for player created from custom element as single target */
4057 content = element_info[element].change_page[i].target_element;
4058 is_player = ELEM_IS_PLAYER(content);
4060 if (is_player && (found_rating < 3 ||
4061 (found_rating == 3 && element < found_element)))
4067 found_element = element;
4072 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4074 /* check for player created from custom element as explosion content */
4075 content = element_info[element].content.e[xx][yy];
4076 is_player = ELEM_IS_PLAYER(content);
4078 if (is_player && (found_rating < 2 ||
4079 (found_rating == 2 && element < found_element)))
4081 start_x = x + xx - 1;
4082 start_y = y + yy - 1;
4085 found_element = element;
4088 if (!CAN_CHANGE(element))
4091 for (i = 0; i < element_info[element].num_change_pages; i++)
4093 /* check for player created from custom element as extended target */
4095 element_info[element].change_page[i].target_content.e[xx][yy];
4097 is_player = ELEM_IS_PLAYER(content);
4099 if (is_player && (found_rating < 1 ||
4100 (found_rating == 1 && element < found_element)))
4102 start_x = x + xx - 1;
4103 start_y = y + yy - 1;
4106 found_element = element;
4112 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4113 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4116 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4117 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4122 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4123 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4124 local_player->jx - MIDPOSX);
4126 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4127 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4128 local_player->jy - MIDPOSY);
4132 /* do not use PLAYING mask for fading out from main screen */
4133 game_status = GAME_MODE_MAIN;
4138 if (!game.restart_level)
4139 CloseDoor(DOOR_CLOSE_1);
4142 if (level_editor_test_game)
4143 FadeSkipNextFadeIn();
4145 FadeSetEnterScreen();
4147 if (level_editor_test_game)
4148 fading = fading_none;
4150 fading = menu.destination;
4154 FadeOut(REDRAW_FIELD);
4157 FadeOut(REDRAW_FIELD);
4161 game_status = GAME_MODE_PLAYING;
4164 /* !!! FIX THIS (START) !!! */
4165 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4167 InitGameEngine_EM();
4169 /* blit playfield from scroll buffer to normal back buffer for fading in */
4170 BlitScreenToBitmap_EM(backbuffer);
4177 /* after drawing the level, correct some elements */
4178 if (game.timegate_time_left == 0)
4179 CloseAllOpenTimegates();
4181 /* blit playfield from scroll buffer to normal back buffer for fading in */
4182 if (setup.soft_scrolling)
4183 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4185 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4187 /* !!! FIX THIS (END) !!! */
4190 FadeIn(REDRAW_FIELD);
4193 FadeIn(REDRAW_FIELD);
4198 if (!game.restart_level)
4200 /* copy default game door content to main double buffer */
4201 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4202 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4205 SetPanelBackground();
4206 SetDrawBackgroundMask(REDRAW_DOOR_1);
4208 UpdateGameDoorValues();
4209 DrawGameDoorValues();
4211 if (!game.restart_level)
4215 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4216 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4217 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4221 /* copy actual game door content to door double buffer for OpenDoor() */
4222 BlitBitmap(drawto, bitmap_db_door,
4223 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4225 OpenDoor(DOOR_OPEN_ALL);
4227 PlaySound(SND_GAME_STARTING);
4229 if (setup.sound_music)
4232 KeyboardAutoRepeatOffUnlessAutoplay();
4236 for (i = 0; i < MAX_PLAYERS; i++)
4237 printf("Player %d %sactive.\n",
4238 i + 1, (stored_player[i].active ? "" : "not "));
4249 game.restart_level = FALSE;
4252 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4254 /* this is used for non-R'n'D game engines to update certain engine values */
4256 /* needed to determine if sounds are played within the visible screen area */
4257 scroll_x = actual_scroll_x;
4258 scroll_y = actual_scroll_y;
4261 void InitMovDir(int x, int y)
4263 int i, element = Feld[x][y];
4264 static int xy[4][2] =
4271 static int direction[3][4] =
4273 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4274 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4275 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4284 Feld[x][y] = EL_BUG;
4285 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4288 case EL_SPACESHIP_RIGHT:
4289 case EL_SPACESHIP_UP:
4290 case EL_SPACESHIP_LEFT:
4291 case EL_SPACESHIP_DOWN:
4292 Feld[x][y] = EL_SPACESHIP;
4293 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4296 case EL_BD_BUTTERFLY_RIGHT:
4297 case EL_BD_BUTTERFLY_UP:
4298 case EL_BD_BUTTERFLY_LEFT:
4299 case EL_BD_BUTTERFLY_DOWN:
4300 Feld[x][y] = EL_BD_BUTTERFLY;
4301 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4304 case EL_BD_FIREFLY_RIGHT:
4305 case EL_BD_FIREFLY_UP:
4306 case EL_BD_FIREFLY_LEFT:
4307 case EL_BD_FIREFLY_DOWN:
4308 Feld[x][y] = EL_BD_FIREFLY;
4309 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4312 case EL_PACMAN_RIGHT:
4314 case EL_PACMAN_LEFT:
4315 case EL_PACMAN_DOWN:
4316 Feld[x][y] = EL_PACMAN;
4317 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4320 case EL_YAMYAM_LEFT:
4321 case EL_YAMYAM_RIGHT:
4323 case EL_YAMYAM_DOWN:
4324 Feld[x][y] = EL_YAMYAM;
4325 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4328 case EL_SP_SNIKSNAK:
4329 MovDir[x][y] = MV_UP;
4332 case EL_SP_ELECTRON:
4333 MovDir[x][y] = MV_LEFT;
4340 Feld[x][y] = EL_MOLE;
4341 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4345 if (IS_CUSTOM_ELEMENT(element))
4347 struct ElementInfo *ei = &element_info[element];
4348 int move_direction_initial = ei->move_direction_initial;
4349 int move_pattern = ei->move_pattern;
4351 if (move_direction_initial == MV_START_PREVIOUS)
4353 if (MovDir[x][y] != MV_NONE)
4356 move_direction_initial = MV_START_AUTOMATIC;
4359 if (move_direction_initial == MV_START_RANDOM)
4360 MovDir[x][y] = 1 << RND(4);
4361 else if (move_direction_initial & MV_ANY_DIRECTION)
4362 MovDir[x][y] = move_direction_initial;
4363 else if (move_pattern == MV_ALL_DIRECTIONS ||
4364 move_pattern == MV_TURNING_LEFT ||
4365 move_pattern == MV_TURNING_RIGHT ||
4366 move_pattern == MV_TURNING_LEFT_RIGHT ||
4367 move_pattern == MV_TURNING_RIGHT_LEFT ||
4368 move_pattern == MV_TURNING_RANDOM)
4369 MovDir[x][y] = 1 << RND(4);
4370 else if (move_pattern == MV_HORIZONTAL)
4371 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4372 else if (move_pattern == MV_VERTICAL)
4373 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4374 else if (move_pattern & MV_ANY_DIRECTION)
4375 MovDir[x][y] = element_info[element].move_pattern;
4376 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4377 move_pattern == MV_ALONG_RIGHT_SIDE)
4379 /* use random direction as default start direction */
4380 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4381 MovDir[x][y] = 1 << RND(4);
4383 for (i = 0; i < NUM_DIRECTIONS; i++)
4385 int x1 = x + xy[i][0];
4386 int y1 = y + xy[i][1];
4388 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4390 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4391 MovDir[x][y] = direction[0][i];
4393 MovDir[x][y] = direction[1][i];
4402 MovDir[x][y] = 1 << RND(4);
4404 if (element != EL_BUG &&
4405 element != EL_SPACESHIP &&
4406 element != EL_BD_BUTTERFLY &&
4407 element != EL_BD_FIREFLY)
4410 for (i = 0; i < NUM_DIRECTIONS; i++)
4412 int x1 = x + xy[i][0];
4413 int y1 = y + xy[i][1];
4415 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4417 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4419 MovDir[x][y] = direction[0][i];
4422 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4423 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4425 MovDir[x][y] = direction[1][i];
4434 GfxDir[x][y] = MovDir[x][y];
4437 void InitAmoebaNr(int x, int y)
4440 int group_nr = AmoebeNachbarNr(x, y);
4444 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4446 if (AmoebaCnt[i] == 0)
4454 AmoebaNr[x][y] = group_nr;
4455 AmoebaCnt[group_nr]++;
4456 AmoebaCnt2[group_nr]++;
4459 static void PlayerWins(struct PlayerInfo *player)
4461 player->LevelSolved = TRUE;
4462 player->GameOver = TRUE;
4464 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4465 level.native_em_level->lev->score : player->score);
4467 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4468 player->LevelSolved_CountingScore = player->score_final;
4473 static int time, time_final;
4474 static int score, score_final;
4475 static int game_over_delay_1 = 0;
4476 static int game_over_delay_2 = 0;
4477 int game_over_delay_value_1 = 50;
4478 int game_over_delay_value_2 = 50;
4480 if (!local_player->LevelSolved_GameWon)
4484 /* do not start end game actions before the player stops moving (to exit) */
4485 if (local_player->MovPos)
4488 local_player->LevelSolved_GameWon = TRUE;
4489 local_player->LevelSolved_SaveTape = tape.recording;
4490 local_player->LevelSolved_SaveScore = !tape.playing;
4492 if (tape.auto_play) /* tape might already be stopped here */
4493 tape.auto_play_level_solved = TRUE;
4499 game_over_delay_1 = game_over_delay_value_1;
4500 game_over_delay_2 = game_over_delay_value_2;
4502 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4503 score = score_final = local_player->score_final;
4508 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4510 else if (level.time == 0 && TimePlayed < 999)
4513 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4516 local_player->score_final = score_final;
4518 if (level_editor_test_game)
4521 score = score_final;
4524 local_player->LevelSolved_CountingTime = time;
4525 local_player->LevelSolved_CountingScore = score;
4527 game_panel_controls[GAME_PANEL_TIME].value = time;
4528 game_panel_controls[GAME_PANEL_SCORE].value = score;
4530 DisplayGameControlValues();
4532 DrawGameValue_Time(time);
4533 DrawGameValue_Score(score);
4537 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4539 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4541 /* close exit door after last player */
4542 if ((AllPlayersGone &&
4543 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4544 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4545 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4546 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4547 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4549 int element = Feld[ExitX][ExitY];
4552 if (element == EL_EM_EXIT_OPEN ||
4553 element == EL_EM_STEEL_EXIT_OPEN)
4560 Feld[ExitX][ExitY] =
4561 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4562 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4563 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4564 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4565 EL_EM_STEEL_EXIT_CLOSING);
4567 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4571 /* player disappears */
4572 DrawLevelField(ExitX, ExitY);
4575 for (i = 0; i < MAX_PLAYERS; i++)
4577 struct PlayerInfo *player = &stored_player[i];
4579 if (player->present)
4581 RemovePlayer(player);
4583 /* player disappears */
4584 DrawLevelField(player->jx, player->jy);
4589 PlaySound(SND_GAME_WINNING);
4592 if (game_over_delay_1 > 0)
4594 game_over_delay_1--;
4599 if (time != time_final)
4601 int time_to_go = ABS(time_final - time);
4602 int time_count_dir = (time < time_final ? +1 : -1);
4603 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4605 time += time_count_steps * time_count_dir;
4606 score += time_count_steps * level.score[SC_TIME_BONUS];
4609 local_player->LevelSolved_CountingTime = time;
4610 local_player->LevelSolved_CountingScore = score;
4612 game_panel_controls[GAME_PANEL_TIME].value = time;
4613 game_panel_controls[GAME_PANEL_SCORE].value = score;
4615 DisplayGameControlValues();
4617 DrawGameValue_Time(time);
4618 DrawGameValue_Score(score);
4621 if (time == time_final)
4622 StopSound(SND_GAME_LEVELTIME_BONUS);
4623 else if (setup.sound_loops)
4624 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4626 PlaySound(SND_GAME_LEVELTIME_BONUS);
4631 local_player->LevelSolved_PanelOff = TRUE;
4633 if (game_over_delay_2 > 0)
4635 game_over_delay_2--;
4648 boolean raise_level = FALSE;
4650 local_player->LevelSolved_GameEnd = TRUE;
4652 CloseDoor(DOOR_CLOSE_1);
4654 if (local_player->LevelSolved_SaveTape)
4661 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4663 SaveTape(tape.level_nr); /* ask to save tape */
4667 if (level_editor_test_game)
4669 game_status = GAME_MODE_MAIN;
4672 DrawAndFadeInMainMenu(REDRAW_FIELD);
4680 if (!local_player->LevelSolved_SaveScore)
4683 FadeOut(REDRAW_FIELD);
4686 game_status = GAME_MODE_MAIN;
4688 DrawAndFadeInMainMenu(REDRAW_FIELD);
4693 if (level_nr == leveldir_current->handicap_level)
4695 leveldir_current->handicap_level++;
4696 SaveLevelSetup_SeriesInfo();
4699 if (level_nr < leveldir_current->last_level)
4700 raise_level = TRUE; /* advance to next level */
4702 if ((hi_pos = NewHiScore()) >= 0)
4704 game_status = GAME_MODE_SCORES;
4706 DrawHallOfFame(hi_pos);
4717 FadeOut(REDRAW_FIELD);
4720 game_status = GAME_MODE_MAIN;
4728 DrawAndFadeInMainMenu(REDRAW_FIELD);
4737 LoadScore(level_nr);
4739 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4740 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4743 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4745 if (local_player->score_final > highscore[k].Score)
4747 /* player has made it to the hall of fame */
4749 if (k < MAX_SCORE_ENTRIES - 1)
4751 int m = MAX_SCORE_ENTRIES - 1;
4754 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4755 if (strEqual(setup.player_name, highscore[l].Name))
4757 if (m == k) /* player's new highscore overwrites his old one */
4761 for (l = m; l > k; l--)
4763 strcpy(highscore[l].Name, highscore[l - 1].Name);
4764 highscore[l].Score = highscore[l - 1].Score;
4771 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4772 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4773 highscore[k].Score = local_player->score_final;
4779 else if (!strncmp(setup.player_name, highscore[k].Name,
4780 MAX_PLAYER_NAME_LEN))
4781 break; /* player already there with a higher score */
4787 SaveScore(level_nr);
4792 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4794 int element = Feld[x][y];
4795 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4796 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4797 int horiz_move = (dx != 0);
4798 int sign = (horiz_move ? dx : dy);
4799 int step = sign * element_info[element].move_stepsize;
4801 /* special values for move stepsize for spring and things on conveyor belt */
4804 if (CAN_FALL(element) &&
4805 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4806 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4807 else if (element == EL_SPRING)
4808 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4814 inline static int getElementMoveStepsize(int x, int y)
4816 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4819 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4821 if (player->GfxAction != action || player->GfxDir != dir)
4824 printf("Player frame reset! (%d => %d, %d => %d)\n",
4825 player->GfxAction, action, player->GfxDir, dir);
4828 player->GfxAction = action;
4829 player->GfxDir = dir;
4831 player->StepFrame = 0;
4835 #if USE_GFX_RESET_GFX_ANIMATION
4836 static void ResetGfxFrame(int x, int y, boolean redraw)
4838 int element = Feld[x][y];
4839 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4840 int last_gfx_frame = GfxFrame[x][y];
4842 if (graphic_info[graphic].anim_global_sync)
4843 GfxFrame[x][y] = FrameCounter;
4844 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4845 GfxFrame[x][y] = CustomValue[x][y];
4846 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4847 GfxFrame[x][y] = element_info[element].collect_score;
4848 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4849 GfxFrame[x][y] = ChangeDelay[x][y];
4851 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4852 DrawLevelGraphicAnimation(x, y, graphic);
4856 static void ResetGfxAnimation(int x, int y)
4858 GfxAction[x][y] = ACTION_DEFAULT;
4859 GfxDir[x][y] = MovDir[x][y];
4862 #if USE_GFX_RESET_GFX_ANIMATION
4863 ResetGfxFrame(x, y, FALSE);
4867 static void ResetRandomAnimationValue(int x, int y)
4869 GfxRandom[x][y] = INIT_GFX_RANDOM();
4872 void InitMovingField(int x, int y, int direction)
4874 int element = Feld[x][y];
4875 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4876 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4879 boolean is_moving_before, is_moving_after;
4881 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4884 /* check if element was/is moving or being moved before/after mode change */
4887 is_moving_before = (WasJustMoving[x][y] != 0);
4889 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4890 is_moving_before = WasJustMoving[x][y];
4893 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4895 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4897 /* reset animation only for moving elements which change direction of moving
4898 or which just started or stopped moving
4899 (else CEs with property "can move" / "not moving" are reset each frame) */
4900 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4902 if (is_moving_before != is_moving_after ||
4903 direction != MovDir[x][y])
4904 ResetGfxAnimation(x, y);
4906 if ((is_moving_before || is_moving_after) && !continues_moving)
4907 ResetGfxAnimation(x, y);
4910 if (!continues_moving)
4911 ResetGfxAnimation(x, y);
4914 MovDir[x][y] = direction;
4915 GfxDir[x][y] = direction;
4917 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4918 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4919 direction == MV_DOWN && CAN_FALL(element) ?
4920 ACTION_FALLING : ACTION_MOVING);
4922 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4923 ACTION_FALLING : ACTION_MOVING);
4926 /* this is needed for CEs with property "can move" / "not moving" */
4928 if (is_moving_after)
4930 if (Feld[newx][newy] == EL_EMPTY)
4931 Feld[newx][newy] = EL_BLOCKED;
4933 MovDir[newx][newy] = MovDir[x][y];
4935 #if USE_NEW_CUSTOM_VALUE
4936 CustomValue[newx][newy] = CustomValue[x][y];
4939 GfxFrame[newx][newy] = GfxFrame[x][y];
4940 GfxRandom[newx][newy] = GfxRandom[x][y];
4941 GfxAction[newx][newy] = GfxAction[x][y];
4942 GfxDir[newx][newy] = GfxDir[x][y];
4946 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4948 int direction = MovDir[x][y];
4949 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4950 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4956 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4958 int oldx = x, oldy = y;
4959 int direction = MovDir[x][y];
4961 if (direction == MV_LEFT)
4963 else if (direction == MV_RIGHT)
4965 else if (direction == MV_UP)
4967 else if (direction == MV_DOWN)
4970 *comes_from_x = oldx;
4971 *comes_from_y = oldy;
4974 int MovingOrBlocked2Element(int x, int y)
4976 int element = Feld[x][y];
4978 if (element == EL_BLOCKED)
4982 Blocked2Moving(x, y, &oldx, &oldy);
4983 return Feld[oldx][oldy];
4989 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4991 /* like MovingOrBlocked2Element(), but if element is moving
4992 and (x,y) is the field the moving element is just leaving,
4993 return EL_BLOCKED instead of the element value */
4994 int element = Feld[x][y];
4996 if (IS_MOVING(x, y))
4998 if (element == EL_BLOCKED)
5002 Blocked2Moving(x, y, &oldx, &oldy);
5003 return Feld[oldx][oldy];
5012 static void RemoveField(int x, int y)
5014 Feld[x][y] = EL_EMPTY;
5020 #if USE_NEW_CUSTOM_VALUE
5021 CustomValue[x][y] = 0;
5025 ChangeDelay[x][y] = 0;
5026 ChangePage[x][y] = -1;
5027 Pushed[x][y] = FALSE;
5030 ExplodeField[x][y] = EX_TYPE_NONE;
5033 GfxElement[x][y] = EL_UNDEFINED;
5034 GfxAction[x][y] = ACTION_DEFAULT;
5035 GfxDir[x][y] = MV_NONE;
5038 void RemoveMovingField(int x, int y)
5040 int oldx = x, oldy = y, newx = x, newy = y;
5041 int element = Feld[x][y];
5042 int next_element = EL_UNDEFINED;
5044 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5047 if (IS_MOVING(x, y))
5049 Moving2Blocked(x, y, &newx, &newy);
5051 if (Feld[newx][newy] != EL_BLOCKED)
5053 /* element is moving, but target field is not free (blocked), but
5054 already occupied by something different (example: acid pool);
5055 in this case, only remove the moving field, but not the target */
5057 RemoveField(oldx, oldy);
5059 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5061 DrawLevelField(oldx, oldy);
5066 else if (element == EL_BLOCKED)
5068 Blocked2Moving(x, y, &oldx, &oldy);
5069 if (!IS_MOVING(oldx, oldy))
5073 if (element == EL_BLOCKED &&
5074 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5075 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5076 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5077 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5078 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5079 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5080 next_element = get_next_element(Feld[oldx][oldy]);
5082 RemoveField(oldx, oldy);
5083 RemoveField(newx, newy);
5085 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5087 if (next_element != EL_UNDEFINED)
5088 Feld[oldx][oldy] = next_element;
5090 DrawLevelField(oldx, oldy);
5091 DrawLevelField(newx, newy);
5094 void DrawDynamite(int x, int y)
5096 int sx = SCREENX(x), sy = SCREENY(y);
5097 int graphic = el2img(Feld[x][y]);
5100 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5103 if (IS_WALKABLE_INSIDE(Back[x][y]))
5107 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5108 else if (Store[x][y])
5109 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5111 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5113 if (Back[x][y] || Store[x][y])
5114 DrawGraphicThruMask(sx, sy, graphic, frame);
5116 DrawGraphic(sx, sy, graphic, frame);
5119 void CheckDynamite(int x, int y)
5121 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5125 if (MovDelay[x][y] != 0)
5128 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5134 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5139 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5141 boolean num_checked_players = 0;
5144 for (i = 0; i < MAX_PLAYERS; i++)
5146 if (stored_player[i].active)
5148 int sx = stored_player[i].jx;
5149 int sy = stored_player[i].jy;
5151 if (num_checked_players == 0)
5158 *sx1 = MIN(*sx1, sx);
5159 *sy1 = MIN(*sy1, sy);
5160 *sx2 = MAX(*sx2, sx);
5161 *sy2 = MAX(*sy2, sy);
5164 num_checked_players++;
5169 static boolean checkIfAllPlayersFitToScreen_RND()
5171 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5173 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5175 return (sx2 - sx1 < SCR_FIELDX &&
5176 sy2 - sy1 < SCR_FIELDY);
5179 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5181 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5183 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5185 *sx = (sx1 + sx2) / 2;
5186 *sy = (sy1 + sy2) / 2;
5189 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5190 boolean center_screen, boolean quick_relocation)
5192 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5193 boolean no_delay = (tape.warp_forward);
5194 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5195 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5197 if (quick_relocation)
5199 int offset = game.scroll_delay_value;
5201 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5203 if (!level.shifted_relocation || center_screen)
5205 /* quick relocation (without scrolling), with centering of screen */
5207 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5208 x > SBX_Right + MIDPOSX ? SBX_Right :
5211 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5212 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5217 /* quick relocation (without scrolling), but do not center screen */
5219 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5220 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5223 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5224 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5227 int offset_x = x + (scroll_x - center_scroll_x);
5228 int offset_y = y + (scroll_y - center_scroll_y);
5230 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5231 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5232 offset_x - MIDPOSX);
5234 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5235 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5236 offset_y - MIDPOSY);
5241 /* quick relocation (without scrolling), inside visible screen area */
5243 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5244 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5245 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5247 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5248 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5249 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5251 /* don't scroll over playfield boundaries */
5252 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5253 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5255 /* don't scroll over playfield boundaries */
5256 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5257 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5260 RedrawPlayfield(TRUE, 0,0,0,0);
5265 int scroll_xx, scroll_yy;
5267 if (!level.shifted_relocation || center_screen)
5269 /* visible relocation (with scrolling), with centering of screen */
5271 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5272 x > SBX_Right + MIDPOSX ? SBX_Right :
5275 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5276 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5281 /* visible relocation (with scrolling), but do not center screen */
5283 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5284 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5287 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5288 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5291 int offset_x = x + (scroll_x - center_scroll_x);
5292 int offset_y = y + (scroll_y - center_scroll_y);
5294 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5295 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5296 offset_x - MIDPOSX);
5298 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5299 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5300 offset_y - MIDPOSY);
5305 /* visible relocation (with scrolling), with centering of screen */
5307 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5308 x > SBX_Right + MIDPOSX ? SBX_Right :
5311 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5312 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5316 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5318 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5321 int fx = FX, fy = FY;
5323 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5324 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5326 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5332 fx += dx * TILEX / 2;
5333 fy += dy * TILEY / 2;
5335 ScrollLevel(dx, dy);
5338 /* scroll in two steps of half tile size to make things smoother */
5339 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5341 Delay(wait_delay_value);
5343 /* scroll second step to align at full tile size */
5345 Delay(wait_delay_value);
5350 Delay(wait_delay_value);
5354 void RelocatePlayer(int jx, int jy, int el_player_raw)
5356 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5357 int player_nr = GET_PLAYER_NR(el_player);
5358 struct PlayerInfo *player = &stored_player[player_nr];
5359 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5360 boolean no_delay = (tape.warp_forward);
5361 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5362 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5363 int old_jx = player->jx;
5364 int old_jy = player->jy;
5365 int old_element = Feld[old_jx][old_jy];
5366 int element = Feld[jx][jy];
5367 boolean player_relocated = (old_jx != jx || old_jy != jy);
5369 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5370 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5371 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5372 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5373 int leave_side_horiz = move_dir_horiz;
5374 int leave_side_vert = move_dir_vert;
5375 int enter_side = enter_side_horiz | enter_side_vert;
5376 int leave_side = leave_side_horiz | leave_side_vert;
5378 if (player->GameOver) /* do not reanimate dead player */
5381 if (!player_relocated) /* no need to relocate the player */
5384 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5386 RemoveField(jx, jy); /* temporarily remove newly placed player */
5387 DrawLevelField(jx, jy);
5390 if (player->present)
5392 while (player->MovPos)
5394 ScrollPlayer(player, SCROLL_GO_ON);
5395 ScrollScreen(NULL, SCROLL_GO_ON);
5397 AdvanceFrameAndPlayerCounters(player->index_nr);
5402 Delay(wait_delay_value);
5405 DrawPlayer(player); /* needed here only to cleanup last field */
5406 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5408 player->is_moving = FALSE;
5411 if (IS_CUSTOM_ELEMENT(old_element))
5412 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5414 player->index_bit, leave_side);
5416 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5418 player->index_bit, leave_side);
5420 Feld[jx][jy] = el_player;
5421 InitPlayerField(jx, jy, el_player, TRUE);
5423 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5425 Feld[jx][jy] = element;
5426 InitField(jx, jy, FALSE);
5429 /* only visually relocate centered player */
5430 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5431 FALSE, level.instant_relocation);
5433 TestIfPlayerTouchesBadThing(jx, jy);
5434 TestIfPlayerTouchesCustomElement(jx, jy);
5436 if (IS_CUSTOM_ELEMENT(element))
5437 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5438 player->index_bit, enter_side);
5440 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5441 player->index_bit, enter_side);
5444 void Explode(int ex, int ey, int phase, int mode)
5450 /* !!! eliminate this variable !!! */
5451 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5453 if (game.explosions_delayed)
5455 ExplodeField[ex][ey] = mode;
5459 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5461 int center_element = Feld[ex][ey];
5462 int artwork_element, explosion_element; /* set these values later */
5465 /* --- This is only really needed (and now handled) in "Impact()". --- */
5466 /* do not explode moving elements that left the explode field in time */
5467 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5468 center_element == EL_EMPTY &&
5469 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5474 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5475 if (mode == EX_TYPE_NORMAL ||
5476 mode == EX_TYPE_CENTER ||
5477 mode == EX_TYPE_CROSS)
5478 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5481 /* remove things displayed in background while burning dynamite */
5482 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5485 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5487 /* put moving element to center field (and let it explode there) */
5488 center_element = MovingOrBlocked2Element(ex, ey);
5489 RemoveMovingField(ex, ey);
5490 Feld[ex][ey] = center_element;
5493 /* now "center_element" is finally determined -- set related values now */
5494 artwork_element = center_element; /* for custom player artwork */
5495 explosion_element = center_element; /* for custom player artwork */
5497 if (IS_PLAYER(ex, ey))
5499 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5501 artwork_element = stored_player[player_nr].artwork_element;
5503 if (level.use_explosion_element[player_nr])
5505 explosion_element = level.explosion_element[player_nr];
5506 artwork_element = explosion_element;
5511 if (mode == EX_TYPE_NORMAL ||
5512 mode == EX_TYPE_CENTER ||
5513 mode == EX_TYPE_CROSS)
5514 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5517 last_phase = element_info[explosion_element].explosion_delay + 1;
5519 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5521 int xx = x - ex + 1;
5522 int yy = y - ey + 1;
5525 if (!IN_LEV_FIELD(x, y) ||
5526 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5527 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5530 element = Feld[x][y];
5532 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5534 element = MovingOrBlocked2Element(x, y);
5536 if (!IS_EXPLOSION_PROOF(element))
5537 RemoveMovingField(x, y);
5540 /* indestructible elements can only explode in center (but not flames) */
5541 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5542 mode == EX_TYPE_BORDER)) ||
5543 element == EL_FLAMES)
5546 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5547 behaviour, for example when touching a yamyam that explodes to rocks
5548 with active deadly shield, a rock is created under the player !!! */
5549 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5551 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5552 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5553 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5555 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5558 if (IS_ACTIVE_BOMB(element))
5560 /* re-activate things under the bomb like gate or penguin */
5561 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5568 /* save walkable background elements while explosion on same tile */
5569 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5570 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5571 Back[x][y] = element;
5573 /* ignite explodable elements reached by other explosion */
5574 if (element == EL_EXPLOSION)
5575 element = Store2[x][y];
5577 if (AmoebaNr[x][y] &&
5578 (element == EL_AMOEBA_FULL ||
5579 element == EL_BD_AMOEBA ||
5580 element == EL_AMOEBA_GROWING))
5582 AmoebaCnt[AmoebaNr[x][y]]--;
5583 AmoebaCnt2[AmoebaNr[x][y]]--;
5588 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5590 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5592 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5594 if (PLAYERINFO(ex, ey)->use_murphy)
5595 Store[x][y] = EL_EMPTY;
5598 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5599 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5600 else if (ELEM_IS_PLAYER(center_element))
5601 Store[x][y] = EL_EMPTY;
5602 else if (center_element == EL_YAMYAM)
5603 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5604 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5605 Store[x][y] = element_info[center_element].content.e[xx][yy];
5607 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5608 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5609 otherwise) -- FIX THIS !!! */
5610 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5611 Store[x][y] = element_info[element].content.e[1][1];
5613 else if (!CAN_EXPLODE(element))
5614 Store[x][y] = element_info[element].content.e[1][1];
5617 Store[x][y] = EL_EMPTY;
5619 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5620 center_element == EL_AMOEBA_TO_DIAMOND)
5621 Store2[x][y] = element;
5623 Feld[x][y] = EL_EXPLOSION;
5624 GfxElement[x][y] = artwork_element;
5626 ExplodePhase[x][y] = 1;
5627 ExplodeDelay[x][y] = last_phase;
5632 if (center_element == EL_YAMYAM)
5633 game.yamyam_content_nr =
5634 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5646 GfxFrame[x][y] = 0; /* restart explosion animation */
5648 last_phase = ExplodeDelay[x][y];
5650 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5654 /* activate this even in non-DEBUG version until cause for crash in
5655 getGraphicAnimationFrame() (see below) is found and eliminated */
5661 /* this can happen if the player leaves an explosion just in time */
5662 if (GfxElement[x][y] == EL_UNDEFINED)
5663 GfxElement[x][y] = EL_EMPTY;
5665 if (GfxElement[x][y] == EL_UNDEFINED)
5668 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5669 printf("Explode(): This should never happen!\n");
5672 GfxElement[x][y] = EL_EMPTY;
5678 border_element = Store2[x][y];
5679 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5680 border_element = StorePlayer[x][y];
5682 if (phase == element_info[border_element].ignition_delay ||
5683 phase == last_phase)
5685 boolean border_explosion = FALSE;
5687 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5688 !PLAYER_EXPLOSION_PROTECTED(x, y))
5690 KillPlayerUnlessExplosionProtected(x, y);
5691 border_explosion = TRUE;
5693 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5695 Feld[x][y] = Store2[x][y];
5698 border_explosion = TRUE;
5700 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5702 AmoebeUmwandeln(x, y);
5704 border_explosion = TRUE;
5707 /* if an element just explodes due to another explosion (chain-reaction),
5708 do not immediately end the new explosion when it was the last frame of
5709 the explosion (as it would be done in the following "if"-statement!) */
5710 if (border_explosion && phase == last_phase)
5714 if (phase == last_phase)
5718 element = Feld[x][y] = Store[x][y];
5719 Store[x][y] = Store2[x][y] = 0;
5720 GfxElement[x][y] = EL_UNDEFINED;
5722 /* player can escape from explosions and might therefore be still alive */
5723 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5724 element <= EL_PLAYER_IS_EXPLODING_4)
5726 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5727 int explosion_element = EL_PLAYER_1 + player_nr;
5728 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5729 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5731 if (level.use_explosion_element[player_nr])
5732 explosion_element = level.explosion_element[player_nr];
5734 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5735 element_info[explosion_element].content.e[xx][yy]);
5738 /* restore probably existing indestructible background element */
5739 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5740 element = Feld[x][y] = Back[x][y];
5743 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5744 GfxDir[x][y] = MV_NONE;
5745 ChangeDelay[x][y] = 0;
5746 ChangePage[x][y] = -1;
5748 #if USE_NEW_CUSTOM_VALUE
5749 CustomValue[x][y] = 0;
5752 InitField_WithBug2(x, y, FALSE);
5754 DrawLevelField(x, y);
5756 TestIfElementTouchesCustomElement(x, y);
5758 if (GFX_CRUMBLED(element))
5759 DrawLevelFieldCrumbledSandNeighbours(x, y);
5761 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5762 StorePlayer[x][y] = 0;
5764 if (ELEM_IS_PLAYER(element))
5765 RelocatePlayer(x, y, element);
5767 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5769 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5770 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5773 DrawLevelFieldCrumbledSand(x, y);
5775 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5777 DrawLevelElement(x, y, Back[x][y]);
5778 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5780 else if (IS_WALKABLE_UNDER(Back[x][y]))
5782 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5783 DrawLevelElementThruMask(x, y, Back[x][y]);
5785 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5786 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5790 void DynaExplode(int ex, int ey)
5793 int dynabomb_element = Feld[ex][ey];
5794 int dynabomb_size = 1;
5795 boolean dynabomb_xl = FALSE;
5796 struct PlayerInfo *player;
5797 static int xy[4][2] =
5805 if (IS_ACTIVE_BOMB(dynabomb_element))
5807 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5808 dynabomb_size = player->dynabomb_size;
5809 dynabomb_xl = player->dynabomb_xl;
5810 player->dynabombs_left++;
5813 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5815 for (i = 0; i < NUM_DIRECTIONS; i++)
5817 for (j = 1; j <= dynabomb_size; j++)
5819 int x = ex + j * xy[i][0];
5820 int y = ey + j * xy[i][1];
5823 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5826 element = Feld[x][y];
5828 /* do not restart explosions of fields with active bombs */
5829 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5832 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5834 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5835 !IS_DIGGABLE(element) && !dynabomb_xl)
5841 void Bang(int x, int y)
5843 int element = MovingOrBlocked2Element(x, y);
5844 int explosion_type = EX_TYPE_NORMAL;
5846 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5848 struct PlayerInfo *player = PLAYERINFO(x, y);
5850 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5851 player->element_nr);
5853 if (level.use_explosion_element[player->index_nr])
5855 int explosion_element = level.explosion_element[player->index_nr];
5857 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5858 explosion_type = EX_TYPE_CROSS;
5859 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5860 explosion_type = EX_TYPE_CENTER;
5868 case EL_BD_BUTTERFLY:
5871 case EL_DARK_YAMYAM:
5875 RaiseScoreElement(element);
5878 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5879 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5880 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5881 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5882 case EL_DYNABOMB_INCREASE_NUMBER:
5883 case EL_DYNABOMB_INCREASE_SIZE:
5884 case EL_DYNABOMB_INCREASE_POWER:
5885 explosion_type = EX_TYPE_DYNA;
5888 case EL_DC_LANDMINE:
5890 case EL_EM_EXIT_OPEN:
5891 case EL_EM_STEEL_EXIT_OPEN:
5893 explosion_type = EX_TYPE_CENTER;
5898 case EL_LAMP_ACTIVE:
5899 case EL_AMOEBA_TO_DIAMOND:
5900 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5901 explosion_type = EX_TYPE_CENTER;
5905 if (element_info[element].explosion_type == EXPLODES_CROSS)
5906 explosion_type = EX_TYPE_CROSS;
5907 else if (element_info[element].explosion_type == EXPLODES_1X1)
5908 explosion_type = EX_TYPE_CENTER;
5912 if (explosion_type == EX_TYPE_DYNA)
5915 Explode(x, y, EX_PHASE_START, explosion_type);
5917 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5920 void SplashAcid(int x, int y)
5922 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5923 (!IN_LEV_FIELD(x - 1, y - 2) ||
5924 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5925 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5927 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5928 (!IN_LEV_FIELD(x + 1, y - 2) ||
5929 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5930 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5932 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5935 static void InitBeltMovement()
5937 static int belt_base_element[4] =
5939 EL_CONVEYOR_BELT_1_LEFT,
5940 EL_CONVEYOR_BELT_2_LEFT,
5941 EL_CONVEYOR_BELT_3_LEFT,
5942 EL_CONVEYOR_BELT_4_LEFT
5944 static int belt_base_active_element[4] =
5946 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5947 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5948 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5949 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5954 /* set frame order for belt animation graphic according to belt direction */
5955 for (i = 0; i < NUM_BELTS; i++)
5959 for (j = 0; j < NUM_BELT_PARTS; j++)
5961 int element = belt_base_active_element[belt_nr] + j;
5962 int graphic_1 = el2img(element);
5963 int graphic_2 = el2panelimg(element);
5965 if (game.belt_dir[i] == MV_LEFT)
5967 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5968 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5972 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5973 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5978 SCAN_PLAYFIELD(x, y)
5980 int element = Feld[x][y];
5982 for (i = 0; i < NUM_BELTS; i++)
5984 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5986 int e_belt_nr = getBeltNrFromBeltElement(element);
5989 if (e_belt_nr == belt_nr)
5991 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5993 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6000 static void ToggleBeltSwitch(int x, int y)
6002 static int belt_base_element[4] =
6004 EL_CONVEYOR_BELT_1_LEFT,
6005 EL_CONVEYOR_BELT_2_LEFT,
6006 EL_CONVEYOR_BELT_3_LEFT,
6007 EL_CONVEYOR_BELT_4_LEFT
6009 static int belt_base_active_element[4] =
6011 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6012 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6013 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6014 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6016 static int belt_base_switch_element[4] =
6018 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6019 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6020 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6021 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6023 static int belt_move_dir[4] =
6031 int element = Feld[x][y];
6032 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6033 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6034 int belt_dir = belt_move_dir[belt_dir_nr];
6037 if (!IS_BELT_SWITCH(element))
6040 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6041 game.belt_dir[belt_nr] = belt_dir;
6043 if (belt_dir_nr == 3)
6046 /* set frame order for belt animation graphic according to belt direction */
6047 for (i = 0; i < NUM_BELT_PARTS; i++)
6049 int element = belt_base_active_element[belt_nr] + i;
6050 int graphic_1 = el2img(element);
6051 int graphic_2 = el2panelimg(element);
6053 if (belt_dir == MV_LEFT)
6055 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6056 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6060 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6061 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6065 SCAN_PLAYFIELD(xx, yy)
6067 int element = Feld[xx][yy];
6069 if (IS_BELT_SWITCH(element))
6071 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6073 if (e_belt_nr == belt_nr)
6075 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6076 DrawLevelField(xx, yy);
6079 else if (IS_BELT(element) && belt_dir != MV_NONE)
6081 int e_belt_nr = getBeltNrFromBeltElement(element);
6083 if (e_belt_nr == belt_nr)
6085 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6087 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6088 DrawLevelField(xx, yy);
6091 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6093 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6095 if (e_belt_nr == belt_nr)
6097 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6099 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6100 DrawLevelField(xx, yy);
6106 static void ToggleSwitchgateSwitch(int x, int y)
6110 game.switchgate_pos = !game.switchgate_pos;
6112 SCAN_PLAYFIELD(xx, yy)
6114 int element = Feld[xx][yy];
6116 #if !USE_BOTH_SWITCHGATE_SWITCHES
6117 if (element == EL_SWITCHGATE_SWITCH_UP ||
6118 element == EL_SWITCHGATE_SWITCH_DOWN)
6120 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6121 DrawLevelField(xx, yy);
6123 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6124 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6126 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6127 DrawLevelField(xx, yy);
6130 if (element == EL_SWITCHGATE_SWITCH_UP)
6132 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6133 DrawLevelField(xx, yy);
6135 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6137 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6138 DrawLevelField(xx, yy);
6140 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6142 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6143 DrawLevelField(xx, yy);
6145 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6147 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6148 DrawLevelField(xx, yy);
6151 else if (element == EL_SWITCHGATE_OPEN ||
6152 element == EL_SWITCHGATE_OPENING)
6154 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6156 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6158 else if (element == EL_SWITCHGATE_CLOSED ||
6159 element == EL_SWITCHGATE_CLOSING)
6161 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6163 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6168 static int getInvisibleActiveFromInvisibleElement(int element)
6170 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6171 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6172 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6176 static int getInvisibleFromInvisibleActiveElement(int element)
6178 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6179 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6180 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6184 static void RedrawAllLightSwitchesAndInvisibleElements()
6188 SCAN_PLAYFIELD(x, y)
6190 int element = Feld[x][y];
6192 if (element == EL_LIGHT_SWITCH &&
6193 game.light_time_left > 0)
6195 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6196 DrawLevelField(x, y);
6198 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6199 game.light_time_left == 0)
6201 Feld[x][y] = EL_LIGHT_SWITCH;
6202 DrawLevelField(x, y);
6204 else if (element == EL_EMC_DRIPPER &&
6205 game.light_time_left > 0)
6207 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6208 DrawLevelField(x, y);
6210 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6211 game.light_time_left == 0)
6213 Feld[x][y] = EL_EMC_DRIPPER;
6214 DrawLevelField(x, y);
6216 else if (element == EL_INVISIBLE_STEELWALL ||
6217 element == EL_INVISIBLE_WALL ||
6218 element == EL_INVISIBLE_SAND)
6220 if (game.light_time_left > 0)
6221 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6223 DrawLevelField(x, y);
6225 /* uncrumble neighbour fields, if needed */
6226 if (element == EL_INVISIBLE_SAND)
6227 DrawLevelFieldCrumbledSandNeighbours(x, y);
6229 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6230 element == EL_INVISIBLE_WALL_ACTIVE ||
6231 element == EL_INVISIBLE_SAND_ACTIVE)
6233 if (game.light_time_left == 0)
6234 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6236 DrawLevelField(x, y);
6238 /* re-crumble neighbour fields, if needed */
6239 if (element == EL_INVISIBLE_SAND)
6240 DrawLevelFieldCrumbledSandNeighbours(x, y);
6245 static void RedrawAllInvisibleElementsForLenses()
6249 SCAN_PLAYFIELD(x, y)
6251 int element = Feld[x][y];
6253 if (element == EL_EMC_DRIPPER &&
6254 game.lenses_time_left > 0)
6256 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6257 DrawLevelField(x, y);
6259 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6260 game.lenses_time_left == 0)
6262 Feld[x][y] = EL_EMC_DRIPPER;
6263 DrawLevelField(x, y);
6265 else if (element == EL_INVISIBLE_STEELWALL ||
6266 element == EL_INVISIBLE_WALL ||
6267 element == EL_INVISIBLE_SAND)
6269 if (game.lenses_time_left > 0)
6270 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6272 DrawLevelField(x, y);
6274 /* uncrumble neighbour fields, if needed */
6275 if (element == EL_INVISIBLE_SAND)
6276 DrawLevelFieldCrumbledSandNeighbours(x, y);
6278 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6279 element == EL_INVISIBLE_WALL_ACTIVE ||
6280 element == EL_INVISIBLE_SAND_ACTIVE)
6282 if (game.lenses_time_left == 0)
6283 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6285 DrawLevelField(x, y);
6287 /* re-crumble neighbour fields, if needed */
6288 if (element == EL_INVISIBLE_SAND)
6289 DrawLevelFieldCrumbledSandNeighbours(x, y);
6294 static void RedrawAllInvisibleElementsForMagnifier()
6298 SCAN_PLAYFIELD(x, y)
6300 int element = Feld[x][y];
6302 if (element == EL_EMC_FAKE_GRASS &&
6303 game.magnify_time_left > 0)
6305 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6306 DrawLevelField(x, y);
6308 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6309 game.magnify_time_left == 0)
6311 Feld[x][y] = EL_EMC_FAKE_GRASS;
6312 DrawLevelField(x, y);
6314 else if (IS_GATE_GRAY(element) &&
6315 game.magnify_time_left > 0)
6317 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6318 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6319 IS_EM_GATE_GRAY(element) ?
6320 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6321 IS_EMC_GATE_GRAY(element) ?
6322 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6324 DrawLevelField(x, y);
6326 else if (IS_GATE_GRAY_ACTIVE(element) &&
6327 game.magnify_time_left == 0)
6329 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6330 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6331 IS_EM_GATE_GRAY_ACTIVE(element) ?
6332 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6333 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6334 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6336 DrawLevelField(x, y);
6341 static void ToggleLightSwitch(int x, int y)
6343 int element = Feld[x][y];
6345 game.light_time_left =
6346 (element == EL_LIGHT_SWITCH ?
6347 level.time_light * FRAMES_PER_SECOND : 0);
6349 RedrawAllLightSwitchesAndInvisibleElements();
6352 static void ActivateTimegateSwitch(int x, int y)
6356 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6358 SCAN_PLAYFIELD(xx, yy)
6360 int element = Feld[xx][yy];
6362 if (element == EL_TIMEGATE_CLOSED ||
6363 element == EL_TIMEGATE_CLOSING)
6365 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6366 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6370 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6372 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6373 DrawLevelField(xx, yy);
6380 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6381 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6383 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6387 void Impact(int x, int y)
6389 boolean last_line = (y == lev_fieldy - 1);
6390 boolean object_hit = FALSE;
6391 boolean impact = (last_line || object_hit);
6392 int element = Feld[x][y];
6393 int smashed = EL_STEELWALL;
6395 if (!last_line) /* check if element below was hit */
6397 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6400 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6401 MovDir[x][y + 1] != MV_DOWN ||
6402 MovPos[x][y + 1] <= TILEY / 2));
6404 /* do not smash moving elements that left the smashed field in time */
6405 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6406 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6409 #if USE_QUICKSAND_IMPACT_BUGFIX
6410 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6412 RemoveMovingField(x, y + 1);
6413 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6414 Feld[x][y + 2] = EL_ROCK;
6415 DrawLevelField(x, y + 2);
6420 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6422 RemoveMovingField(x, y + 1);
6423 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6424 Feld[x][y + 2] = EL_ROCK;
6425 DrawLevelField(x, y + 2);
6432 smashed = MovingOrBlocked2Element(x, y + 1);
6434 impact = (last_line || object_hit);
6437 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6439 SplashAcid(x, y + 1);
6443 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6444 /* only reset graphic animation if graphic really changes after impact */
6446 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6448 ResetGfxAnimation(x, y);
6449 DrawLevelField(x, y);
6452 if (impact && CAN_EXPLODE_IMPACT(element))
6457 else if (impact && element == EL_PEARL &&
6458 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6460 ResetGfxAnimation(x, y);
6462 Feld[x][y] = EL_PEARL_BREAKING;
6463 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6466 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6468 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6473 if (impact && element == EL_AMOEBA_DROP)
6475 if (object_hit && IS_PLAYER(x, y + 1))
6476 KillPlayerUnlessEnemyProtected(x, y + 1);
6477 else if (object_hit && smashed == EL_PENGUIN)
6481 Feld[x][y] = EL_AMOEBA_GROWING;
6482 Store[x][y] = EL_AMOEBA_WET;
6484 ResetRandomAnimationValue(x, y);
6489 if (object_hit) /* check which object was hit */
6491 if ((CAN_PASS_MAGIC_WALL(element) &&
6492 (smashed == EL_MAGIC_WALL ||
6493 smashed == EL_BD_MAGIC_WALL)) ||
6494 (CAN_PASS_DC_MAGIC_WALL(element) &&
6495 smashed == EL_DC_MAGIC_WALL))
6498 int activated_magic_wall =
6499 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6500 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6501 EL_DC_MAGIC_WALL_ACTIVE);
6503 /* activate magic wall / mill */
6504 SCAN_PLAYFIELD(xx, yy)
6506 if (Feld[xx][yy] == smashed)
6507 Feld[xx][yy] = activated_magic_wall;
6510 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6511 game.magic_wall_active = TRUE;
6513 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6514 SND_MAGIC_WALL_ACTIVATING :
6515 smashed == EL_BD_MAGIC_WALL ?
6516 SND_BD_MAGIC_WALL_ACTIVATING :
6517 SND_DC_MAGIC_WALL_ACTIVATING));
6520 if (IS_PLAYER(x, y + 1))
6522 if (CAN_SMASH_PLAYER(element))
6524 KillPlayerUnlessEnemyProtected(x, y + 1);
6528 else if (smashed == EL_PENGUIN)
6530 if (CAN_SMASH_PLAYER(element))
6536 else if (element == EL_BD_DIAMOND)
6538 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6544 else if (((element == EL_SP_INFOTRON ||
6545 element == EL_SP_ZONK) &&
6546 (smashed == EL_SP_SNIKSNAK ||
6547 smashed == EL_SP_ELECTRON ||
6548 smashed == EL_SP_DISK_ORANGE)) ||
6549 (element == EL_SP_INFOTRON &&
6550 smashed == EL_SP_DISK_YELLOW))
6555 else if (CAN_SMASH_EVERYTHING(element))
6557 if (IS_CLASSIC_ENEMY(smashed) ||
6558 CAN_EXPLODE_SMASHED(smashed))
6563 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6565 if (smashed == EL_LAMP ||
6566 smashed == EL_LAMP_ACTIVE)
6571 else if (smashed == EL_NUT)
6573 Feld[x][y + 1] = EL_NUT_BREAKING;
6574 PlayLevelSound(x, y, SND_NUT_BREAKING);
6575 RaiseScoreElement(EL_NUT);
6578 else if (smashed == EL_PEARL)
6580 ResetGfxAnimation(x, y);
6582 Feld[x][y + 1] = EL_PEARL_BREAKING;
6583 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6586 else if (smashed == EL_DIAMOND)
6588 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6589 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6592 else if (IS_BELT_SWITCH(smashed))
6594 ToggleBeltSwitch(x, y + 1);
6596 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6597 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6598 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6599 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6601 ToggleSwitchgateSwitch(x, y + 1);
6603 else if (smashed == EL_LIGHT_SWITCH ||
6604 smashed == EL_LIGHT_SWITCH_ACTIVE)
6606 ToggleLightSwitch(x, y + 1);
6611 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6614 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6616 CheckElementChangeBySide(x, y + 1, smashed, element,
6617 CE_SWITCHED, CH_SIDE_TOP);
6618 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6624 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6629 /* play sound of magic wall / mill */
6631 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6632 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6633 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6635 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6636 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6637 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6638 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6639 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6640 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6645 /* play sound of object that hits the ground */
6646 if (last_line || object_hit)
6647 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6650 inline static void TurnRoundExt(int x, int y)
6662 { 0, 0 }, { 0, 0 }, { 0, 0 },
6667 int left, right, back;
6671 { MV_DOWN, MV_UP, MV_RIGHT },
6672 { MV_UP, MV_DOWN, MV_LEFT },
6674 { MV_LEFT, MV_RIGHT, MV_DOWN },
6678 { MV_RIGHT, MV_LEFT, MV_UP }
6681 int element = Feld[x][y];
6682 int move_pattern = element_info[element].move_pattern;
6684 int old_move_dir = MovDir[x][y];
6685 int left_dir = turn[old_move_dir].left;
6686 int right_dir = turn[old_move_dir].right;
6687 int back_dir = turn[old_move_dir].back;
6689 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6690 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6691 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6692 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6694 int left_x = x + left_dx, left_y = y + left_dy;
6695 int right_x = x + right_dx, right_y = y + right_dy;
6696 int move_x = x + move_dx, move_y = y + move_dy;
6700 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6702 TestIfBadThingTouchesOtherBadThing(x, y);
6704 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6705 MovDir[x][y] = right_dir;
6706 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6707 MovDir[x][y] = left_dir;
6709 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6711 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6714 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6716 TestIfBadThingTouchesOtherBadThing(x, y);
6718 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6719 MovDir[x][y] = left_dir;
6720 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6721 MovDir[x][y] = right_dir;
6723 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6725 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6728 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6730 TestIfBadThingTouchesOtherBadThing(x, y);
6732 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6733 MovDir[x][y] = left_dir;
6734 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6735 MovDir[x][y] = right_dir;
6737 if (MovDir[x][y] != old_move_dir)
6740 else if (element == EL_YAMYAM)
6742 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6743 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6745 if (can_turn_left && can_turn_right)
6746 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6747 else if (can_turn_left)
6748 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6749 else if (can_turn_right)
6750 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6752 MovDir[x][y] = back_dir;
6754 MovDelay[x][y] = 16 + 16 * RND(3);
6756 else if (element == EL_DARK_YAMYAM)
6758 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6760 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6763 if (can_turn_left && can_turn_right)
6764 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6765 else if (can_turn_left)
6766 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6767 else if (can_turn_right)
6768 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6770 MovDir[x][y] = back_dir;
6772 MovDelay[x][y] = 16 + 16 * RND(3);
6774 else if (element == EL_PACMAN)
6776 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6777 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6779 if (can_turn_left && can_turn_right)
6780 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6781 else if (can_turn_left)
6782 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6783 else if (can_turn_right)
6784 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6786 MovDir[x][y] = back_dir;
6788 MovDelay[x][y] = 6 + RND(40);
6790 else if (element == EL_PIG)
6792 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6793 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6794 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6795 boolean should_turn_left, should_turn_right, should_move_on;
6797 int rnd = RND(rnd_value);
6799 should_turn_left = (can_turn_left &&
6801 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6802 y + back_dy + left_dy)));
6803 should_turn_right = (can_turn_right &&
6805 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6806 y + back_dy + right_dy)));
6807 should_move_on = (can_move_on &&
6810 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6811 y + move_dy + left_dy) ||
6812 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6813 y + move_dy + right_dy)));
6815 if (should_turn_left || should_turn_right || should_move_on)
6817 if (should_turn_left && should_turn_right && should_move_on)
6818 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6819 rnd < 2 * rnd_value / 3 ? right_dir :
6821 else if (should_turn_left && should_turn_right)
6822 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6823 else if (should_turn_left && should_move_on)
6824 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6825 else if (should_turn_right && should_move_on)
6826 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6827 else if (should_turn_left)
6828 MovDir[x][y] = left_dir;
6829 else if (should_turn_right)
6830 MovDir[x][y] = right_dir;
6831 else if (should_move_on)
6832 MovDir[x][y] = old_move_dir;
6834 else if (can_move_on && rnd > rnd_value / 8)
6835 MovDir[x][y] = old_move_dir;
6836 else if (can_turn_left && can_turn_right)
6837 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6838 else if (can_turn_left && rnd > rnd_value / 8)
6839 MovDir[x][y] = left_dir;
6840 else if (can_turn_right && rnd > rnd_value/8)
6841 MovDir[x][y] = right_dir;
6843 MovDir[x][y] = back_dir;
6845 xx = x + move_xy[MovDir[x][y]].dx;
6846 yy = y + move_xy[MovDir[x][y]].dy;
6848 if (!IN_LEV_FIELD(xx, yy) ||
6849 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6850 MovDir[x][y] = old_move_dir;
6854 else if (element == EL_DRAGON)
6856 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6857 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6858 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6860 int rnd = RND(rnd_value);
6862 if (can_move_on && rnd > rnd_value / 8)
6863 MovDir[x][y] = old_move_dir;
6864 else if (can_turn_left && can_turn_right)
6865 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6866 else if (can_turn_left && rnd > rnd_value / 8)
6867 MovDir[x][y] = left_dir;
6868 else if (can_turn_right && rnd > rnd_value / 8)
6869 MovDir[x][y] = right_dir;
6871 MovDir[x][y] = back_dir;
6873 xx = x + move_xy[MovDir[x][y]].dx;
6874 yy = y + move_xy[MovDir[x][y]].dy;
6876 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6877 MovDir[x][y] = old_move_dir;
6881 else if (element == EL_MOLE)
6883 boolean can_move_on =
6884 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6885 IS_AMOEBOID(Feld[move_x][move_y]) ||
6886 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6889 boolean can_turn_left =
6890 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6891 IS_AMOEBOID(Feld[left_x][left_y])));
6893 boolean can_turn_right =
6894 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6895 IS_AMOEBOID(Feld[right_x][right_y])));
6897 if (can_turn_left && can_turn_right)
6898 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6899 else if (can_turn_left)
6900 MovDir[x][y] = left_dir;
6902 MovDir[x][y] = right_dir;
6905 if (MovDir[x][y] != old_move_dir)
6908 else if (element == EL_BALLOON)
6910 MovDir[x][y] = game.wind_direction;
6913 else if (element == EL_SPRING)
6915 #if USE_NEW_SPRING_BUMPER
6916 if (MovDir[x][y] & MV_HORIZONTAL)
6918 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6919 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6921 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6922 ResetGfxAnimation(move_x, move_y);
6923 DrawLevelField(move_x, move_y);
6925 MovDir[x][y] = back_dir;
6927 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6928 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6929 MovDir[x][y] = MV_NONE;
6932 if (MovDir[x][y] & MV_HORIZONTAL &&
6933 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6934 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6935 MovDir[x][y] = MV_NONE;
6940 else if (element == EL_ROBOT ||
6941 element == EL_SATELLITE ||
6942 element == EL_PENGUIN ||
6943 element == EL_EMC_ANDROID)
6945 int attr_x = -1, attr_y = -1;
6956 for (i = 0; i < MAX_PLAYERS; i++)
6958 struct PlayerInfo *player = &stored_player[i];
6959 int jx = player->jx, jy = player->jy;
6961 if (!player->active)
6965 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6973 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6974 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6975 game.engine_version < VERSION_IDENT(3,1,0,0)))
6981 if (element == EL_PENGUIN)
6984 static int xy[4][2] =
6992 for (i = 0; i < NUM_DIRECTIONS; i++)
6994 int ex = x + xy[i][0];
6995 int ey = y + xy[i][1];
6997 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6998 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6999 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7000 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7009 MovDir[x][y] = MV_NONE;
7011 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7012 else if (attr_x > x)
7013 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7015 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7016 else if (attr_y > y)
7017 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7019 if (element == EL_ROBOT)
7023 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7024 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7025 Moving2Blocked(x, y, &newx, &newy);
7027 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7028 MovDelay[x][y] = 8 + 8 * !RND(3);
7030 MovDelay[x][y] = 16;
7032 else if (element == EL_PENGUIN)
7038 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7040 boolean first_horiz = RND(2);
7041 int new_move_dir = MovDir[x][y];
7044 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7045 Moving2Blocked(x, y, &newx, &newy);
7047 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7051 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7052 Moving2Blocked(x, y, &newx, &newy);
7054 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7057 MovDir[x][y] = old_move_dir;
7061 else if (element == EL_SATELLITE)
7067 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7069 boolean first_horiz = RND(2);
7070 int new_move_dir = MovDir[x][y];
7073 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7074 Moving2Blocked(x, y, &newx, &newy);
7076 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7080 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7081 Moving2Blocked(x, y, &newx, &newy);
7083 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7086 MovDir[x][y] = old_move_dir;
7090 else if (element == EL_EMC_ANDROID)
7092 static int check_pos[16] =
7094 -1, /* 0 => (invalid) */
7095 7, /* 1 => MV_LEFT */
7096 3, /* 2 => MV_RIGHT */
7097 -1, /* 3 => (invalid) */
7099 0, /* 5 => MV_LEFT | MV_UP */
7100 2, /* 6 => MV_RIGHT | MV_UP */
7101 -1, /* 7 => (invalid) */
7102 5, /* 8 => MV_DOWN */
7103 6, /* 9 => MV_LEFT | MV_DOWN */
7104 4, /* 10 => MV_RIGHT | MV_DOWN */
7105 -1, /* 11 => (invalid) */
7106 -1, /* 12 => (invalid) */
7107 -1, /* 13 => (invalid) */
7108 -1, /* 14 => (invalid) */
7109 -1, /* 15 => (invalid) */
7117 { -1, -1, MV_LEFT | MV_UP },
7119 { +1, -1, MV_RIGHT | MV_UP },
7120 { +1, 0, MV_RIGHT },
7121 { +1, +1, MV_RIGHT | MV_DOWN },
7123 { -1, +1, MV_LEFT | MV_DOWN },
7126 int start_pos, check_order;
7127 boolean can_clone = FALSE;
7130 /* check if there is any free field around current position */
7131 for (i = 0; i < 8; i++)
7133 int newx = x + check_xy[i].dx;
7134 int newy = y + check_xy[i].dy;
7136 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7144 if (can_clone) /* randomly find an element to clone */
7148 start_pos = check_pos[RND(8)];
7149 check_order = (RND(2) ? -1 : +1);
7151 for (i = 0; i < 8; i++)
7153 int pos_raw = start_pos + i * check_order;
7154 int pos = (pos_raw + 8) % 8;
7155 int newx = x + check_xy[pos].dx;
7156 int newy = y + check_xy[pos].dy;
7158 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7160 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7161 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7163 Store[x][y] = Feld[newx][newy];
7172 if (can_clone) /* randomly find a direction to move */
7176 start_pos = check_pos[RND(8)];
7177 check_order = (RND(2) ? -1 : +1);
7179 for (i = 0; i < 8; i++)
7181 int pos_raw = start_pos + i * check_order;
7182 int pos = (pos_raw + 8) % 8;
7183 int newx = x + check_xy[pos].dx;
7184 int newy = y + check_xy[pos].dy;
7185 int new_move_dir = check_xy[pos].dir;
7187 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7189 MovDir[x][y] = new_move_dir;
7190 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7199 if (can_clone) /* cloning and moving successful */
7202 /* cannot clone -- try to move towards player */
7204 start_pos = check_pos[MovDir[x][y] & 0x0f];
7205 check_order = (RND(2) ? -1 : +1);
7207 for (i = 0; i < 3; i++)
7209 /* first check start_pos, then previous/next or (next/previous) pos */
7210 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7211 int pos = (pos_raw + 8) % 8;
7212 int newx = x + check_xy[pos].dx;
7213 int newy = y + check_xy[pos].dy;
7214 int new_move_dir = check_xy[pos].dir;
7216 if (IS_PLAYER(newx, newy))
7219 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7221 MovDir[x][y] = new_move_dir;
7222 MovDelay[x][y] = level.android_move_time * 8 + 1;
7229 else if (move_pattern == MV_TURNING_LEFT ||
7230 move_pattern == MV_TURNING_RIGHT ||
7231 move_pattern == MV_TURNING_LEFT_RIGHT ||
7232 move_pattern == MV_TURNING_RIGHT_LEFT ||
7233 move_pattern == MV_TURNING_RANDOM ||
7234 move_pattern == MV_ALL_DIRECTIONS)
7236 boolean can_turn_left =
7237 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7238 boolean can_turn_right =
7239 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7241 if (element_info[element].move_stepsize == 0) /* "not moving" */
7244 if (move_pattern == MV_TURNING_LEFT)
7245 MovDir[x][y] = left_dir;
7246 else if (move_pattern == MV_TURNING_RIGHT)
7247 MovDir[x][y] = right_dir;
7248 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7249 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7250 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7251 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7252 else if (move_pattern == MV_TURNING_RANDOM)
7253 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7254 can_turn_right && !can_turn_left ? right_dir :
7255 RND(2) ? left_dir : right_dir);
7256 else if (can_turn_left && can_turn_right)
7257 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7258 else if (can_turn_left)
7259 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7260 else if (can_turn_right)
7261 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7263 MovDir[x][y] = back_dir;
7265 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7267 else if (move_pattern == MV_HORIZONTAL ||
7268 move_pattern == MV_VERTICAL)
7270 if (move_pattern & old_move_dir)
7271 MovDir[x][y] = back_dir;
7272 else if (move_pattern == MV_HORIZONTAL)
7273 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7274 else if (move_pattern == MV_VERTICAL)
7275 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7277 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7279 else if (move_pattern & MV_ANY_DIRECTION)
7281 MovDir[x][y] = move_pattern;
7282 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7284 else if (move_pattern & MV_WIND_DIRECTION)
7286 MovDir[x][y] = game.wind_direction;
7287 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7289 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7291 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7292 MovDir[x][y] = left_dir;
7293 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7294 MovDir[x][y] = right_dir;
7296 if (MovDir[x][y] != old_move_dir)
7297 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7299 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7301 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7302 MovDir[x][y] = right_dir;
7303 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7304 MovDir[x][y] = left_dir;
7306 if (MovDir[x][y] != old_move_dir)
7307 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7309 else if (move_pattern == MV_TOWARDS_PLAYER ||
7310 move_pattern == MV_AWAY_FROM_PLAYER)
7312 int attr_x = -1, attr_y = -1;
7314 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7325 for (i = 0; i < MAX_PLAYERS; i++)
7327 struct PlayerInfo *player = &stored_player[i];
7328 int jx = player->jx, jy = player->jy;
7330 if (!player->active)
7334 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7342 MovDir[x][y] = MV_NONE;
7344 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7345 else if (attr_x > x)
7346 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7348 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7349 else if (attr_y > y)
7350 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7352 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7354 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7356 boolean first_horiz = RND(2);
7357 int new_move_dir = MovDir[x][y];
7359 if (element_info[element].move_stepsize == 0) /* "not moving" */
7361 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7362 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369 Moving2Blocked(x, y, &newx, &newy);
7371 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7375 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7376 Moving2Blocked(x, y, &newx, &newy);
7378 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7381 MovDir[x][y] = old_move_dir;
7384 else if (move_pattern == MV_WHEN_PUSHED ||
7385 move_pattern == MV_WHEN_DROPPED)
7387 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7388 MovDir[x][y] = MV_NONE;
7392 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7394 static int test_xy[7][2] =
7404 static int test_dir[7] =
7414 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7415 int move_preference = -1000000; /* start with very low preference */
7416 int new_move_dir = MV_NONE;
7417 int start_test = RND(4);
7420 for (i = 0; i < NUM_DIRECTIONS; i++)
7422 int move_dir = test_dir[start_test + i];
7423 int move_dir_preference;
7425 xx = x + test_xy[start_test + i][0];
7426 yy = y + test_xy[start_test + i][1];
7428 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7429 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7431 new_move_dir = move_dir;
7436 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7439 move_dir_preference = -1 * RunnerVisit[xx][yy];
7440 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7441 move_dir_preference = PlayerVisit[xx][yy];
7443 if (move_dir_preference > move_preference)
7445 /* prefer field that has not been visited for the longest time */
7446 move_preference = move_dir_preference;
7447 new_move_dir = move_dir;
7449 else if (move_dir_preference == move_preference &&
7450 move_dir == old_move_dir)
7452 /* prefer last direction when all directions are preferred equally */
7453 move_preference = move_dir_preference;
7454 new_move_dir = move_dir;
7458 MovDir[x][y] = new_move_dir;
7459 if (old_move_dir != new_move_dir)
7460 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7464 static void TurnRound(int x, int y)
7466 int direction = MovDir[x][y];
7470 GfxDir[x][y] = MovDir[x][y];
7472 if (direction != MovDir[x][y])
7476 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7478 ResetGfxFrame(x, y, FALSE);
7481 static boolean JustBeingPushed(int x, int y)
7485 for (i = 0; i < MAX_PLAYERS; i++)
7487 struct PlayerInfo *player = &stored_player[i];
7489 if (player->active && player->is_pushing && player->MovPos)
7491 int next_jx = player->jx + (player->jx - player->last_jx);
7492 int next_jy = player->jy + (player->jy - player->last_jy);
7494 if (x == next_jx && y == next_jy)
7502 void StartMoving(int x, int y)
7504 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7505 int element = Feld[x][y];
7510 if (MovDelay[x][y] == 0)
7511 GfxAction[x][y] = ACTION_DEFAULT;
7513 if (CAN_FALL(element) && y < lev_fieldy - 1)
7515 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7516 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7517 if (JustBeingPushed(x, y))
7520 if (element == EL_QUICKSAND_FULL)
7522 if (IS_FREE(x, y + 1))
7524 InitMovingField(x, y, MV_DOWN);
7525 started_moving = TRUE;
7527 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7528 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7529 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7530 Store[x][y] = EL_ROCK;
7532 Store[x][y] = EL_ROCK;
7535 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7537 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7539 if (!MovDelay[x][y])
7540 MovDelay[x][y] = TILEY + 1;
7549 Feld[x][y] = EL_QUICKSAND_EMPTY;
7550 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7551 Store[x][y + 1] = Store[x][y];
7554 PlayLevelSoundAction(x, y, ACTION_FILLING);
7557 else if (element == EL_QUICKSAND_FAST_FULL)
7559 if (IS_FREE(x, y + 1))
7561 InitMovingField(x, y, MV_DOWN);
7562 started_moving = TRUE;
7564 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7565 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7566 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7567 Store[x][y] = EL_ROCK;
7569 Store[x][y] = EL_ROCK;
7572 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7574 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7576 if (!MovDelay[x][y])
7577 MovDelay[x][y] = TILEY + 1;
7586 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7587 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7588 Store[x][y + 1] = Store[x][y];
7591 PlayLevelSoundAction(x, y, ACTION_FILLING);
7594 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7595 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7597 InitMovingField(x, y, MV_DOWN);
7598 started_moving = TRUE;
7600 Feld[x][y] = EL_QUICKSAND_FILLING;
7601 Store[x][y] = element;
7603 PlayLevelSoundAction(x, y, ACTION_FILLING);
7605 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7606 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7608 InitMovingField(x, y, MV_DOWN);
7609 started_moving = TRUE;
7611 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7612 Store[x][y] = element;
7614 PlayLevelSoundAction(x, y, ACTION_FILLING);
7616 else if (element == EL_MAGIC_WALL_FULL)
7618 if (IS_FREE(x, y + 1))
7620 InitMovingField(x, y, MV_DOWN);
7621 started_moving = TRUE;
7623 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7624 Store[x][y] = EL_CHANGED(Store[x][y]);
7626 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7628 if (!MovDelay[x][y])
7629 MovDelay[x][y] = TILEY/4 + 1;
7638 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7639 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7640 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7644 else if (element == EL_BD_MAGIC_WALL_FULL)
7646 if (IS_FREE(x, y + 1))
7648 InitMovingField(x, y, MV_DOWN);
7649 started_moving = TRUE;
7651 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7652 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7654 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7656 if (!MovDelay[x][y])
7657 MovDelay[x][y] = TILEY/4 + 1;
7666 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7667 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7668 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7672 else if (element == EL_DC_MAGIC_WALL_FULL)
7674 if (IS_FREE(x, y + 1))
7676 InitMovingField(x, y, MV_DOWN);
7677 started_moving = TRUE;
7679 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7680 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7682 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7684 if (!MovDelay[x][y])
7685 MovDelay[x][y] = TILEY/4 + 1;
7694 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7695 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7696 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7700 else if ((CAN_PASS_MAGIC_WALL(element) &&
7701 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7702 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7703 (CAN_PASS_DC_MAGIC_WALL(element) &&
7704 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7707 InitMovingField(x, y, MV_DOWN);
7708 started_moving = TRUE;
7711 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7712 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7713 EL_DC_MAGIC_WALL_FILLING);
7714 Store[x][y] = element;
7716 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7718 SplashAcid(x, y + 1);
7720 InitMovingField(x, y, MV_DOWN);
7721 started_moving = TRUE;
7723 Store[x][y] = EL_ACID;
7726 #if USE_FIX_IMPACT_COLLISION
7727 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7728 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7730 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7731 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7733 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7734 CAN_FALL(element) && WasJustFalling[x][y] &&
7735 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7737 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7738 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7739 (Feld[x][y + 1] == EL_BLOCKED)))
7741 /* this is needed for a special case not covered by calling "Impact()"
7742 from "ContinueMoving()": if an element moves to a tile directly below
7743 another element which was just falling on that tile (which was empty
7744 in the previous frame), the falling element above would just stop
7745 instead of smashing the element below (in previous version, the above
7746 element was just checked for "moving" instead of "falling", resulting
7747 in incorrect smashes caused by horizontal movement of the above
7748 element; also, the case of the player being the element to smash was
7749 simply not covered here... :-/ ) */
7751 CheckCollision[x][y] = 0;
7752 CheckImpact[x][y] = 0;
7756 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7758 if (MovDir[x][y] == MV_NONE)
7760 InitMovingField(x, y, MV_DOWN);
7761 started_moving = TRUE;
7764 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7766 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7767 MovDir[x][y] = MV_DOWN;
7769 InitMovingField(x, y, MV_DOWN);
7770 started_moving = TRUE;
7772 else if (element == EL_AMOEBA_DROP)
7774 Feld[x][y] = EL_AMOEBA_GROWING;
7775 Store[x][y] = EL_AMOEBA_WET;
7777 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7778 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7779 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7780 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7782 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7783 (IS_FREE(x - 1, y + 1) ||
7784 Feld[x - 1][y + 1] == EL_ACID));
7785 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7786 (IS_FREE(x + 1, y + 1) ||
7787 Feld[x + 1][y + 1] == EL_ACID));
7788 boolean can_fall_any = (can_fall_left || can_fall_right);
7789 boolean can_fall_both = (can_fall_left && can_fall_right);
7790 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7792 #if USE_NEW_ALL_SLIPPERY
7793 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7795 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7796 can_fall_right = FALSE;
7797 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7798 can_fall_left = FALSE;
7799 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7800 can_fall_right = FALSE;
7801 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7802 can_fall_left = FALSE;
7804 can_fall_any = (can_fall_left || can_fall_right);
7805 can_fall_both = FALSE;
7808 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7810 if (slippery_type == SLIPPERY_ONLY_LEFT)
7811 can_fall_right = FALSE;
7812 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7813 can_fall_left = FALSE;
7814 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7815 can_fall_right = FALSE;
7816 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7817 can_fall_left = FALSE;
7819 can_fall_any = (can_fall_left || can_fall_right);
7820 can_fall_both = (can_fall_left && can_fall_right);
7824 #if USE_NEW_ALL_SLIPPERY
7826 #if USE_NEW_SP_SLIPPERY
7827 /* !!! better use the same properties as for custom elements here !!! */
7828 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7829 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7831 can_fall_right = FALSE; /* slip down on left side */
7832 can_fall_both = FALSE;
7837 #if USE_NEW_ALL_SLIPPERY
7840 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7841 can_fall_right = FALSE; /* slip down on left side */
7843 can_fall_left = !(can_fall_right = RND(2));
7845 can_fall_both = FALSE;
7850 if (game.emulation == EMU_BOULDERDASH ||
7851 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7852 can_fall_right = FALSE; /* slip down on left side */
7854 can_fall_left = !(can_fall_right = RND(2));
7856 can_fall_both = FALSE;
7862 /* if not determined otherwise, prefer left side for slipping down */
7863 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7864 started_moving = TRUE;
7868 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7870 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7873 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7874 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7875 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7876 int belt_dir = game.belt_dir[belt_nr];
7878 if ((belt_dir == MV_LEFT && left_is_free) ||
7879 (belt_dir == MV_RIGHT && right_is_free))
7881 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7883 InitMovingField(x, y, belt_dir);
7884 started_moving = TRUE;
7886 Pushed[x][y] = TRUE;
7887 Pushed[nextx][y] = TRUE;
7889 GfxAction[x][y] = ACTION_DEFAULT;
7893 MovDir[x][y] = 0; /* if element was moving, stop it */
7898 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7900 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7902 if (CAN_MOVE(element) && !started_moving)
7905 int move_pattern = element_info[element].move_pattern;
7910 if (MovDir[x][y] == MV_NONE)
7912 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7913 x, y, element, element_info[element].token_name);
7914 printf("StartMoving(): This should never happen!\n");
7919 Moving2Blocked(x, y, &newx, &newy);
7921 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7924 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7925 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7927 WasJustMoving[x][y] = 0;
7928 CheckCollision[x][y] = 0;
7930 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7932 if (Feld[x][y] != element) /* element has changed */
7936 if (!MovDelay[x][y]) /* start new movement phase */
7938 /* all objects that can change their move direction after each step
7939 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7941 if (element != EL_YAMYAM &&
7942 element != EL_DARK_YAMYAM &&
7943 element != EL_PACMAN &&
7944 !(move_pattern & MV_ANY_DIRECTION) &&
7945 move_pattern != MV_TURNING_LEFT &&
7946 move_pattern != MV_TURNING_RIGHT &&
7947 move_pattern != MV_TURNING_LEFT_RIGHT &&
7948 move_pattern != MV_TURNING_RIGHT_LEFT &&
7949 move_pattern != MV_TURNING_RANDOM)
7953 if (MovDelay[x][y] && (element == EL_BUG ||
7954 element == EL_SPACESHIP ||
7955 element == EL_SP_SNIKSNAK ||
7956 element == EL_SP_ELECTRON ||
7957 element == EL_MOLE))
7958 DrawLevelField(x, y);
7962 if (MovDelay[x][y]) /* wait some time before next movement */
7966 if (element == EL_ROBOT ||
7967 element == EL_YAMYAM ||
7968 element == EL_DARK_YAMYAM)
7970 DrawLevelElementAnimationIfNeeded(x, y, element);
7971 PlayLevelSoundAction(x, y, ACTION_WAITING);
7973 else if (element == EL_SP_ELECTRON)
7974 DrawLevelElementAnimationIfNeeded(x, y, element);
7975 else if (element == EL_DRAGON)
7978 int dir = MovDir[x][y];
7979 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7980 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7981 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7982 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7983 dir == MV_UP ? IMG_FLAMES_1_UP :
7984 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7985 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7987 GfxAction[x][y] = ACTION_ATTACKING;
7989 if (IS_PLAYER(x, y))
7990 DrawPlayerField(x, y);
7992 DrawLevelField(x, y);
7994 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7996 for (i = 1; i <= 3; i++)
7998 int xx = x + i * dx;
7999 int yy = y + i * dy;
8000 int sx = SCREENX(xx);
8001 int sy = SCREENY(yy);
8002 int flame_graphic = graphic + (i - 1);
8004 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8009 int flamed = MovingOrBlocked2Element(xx, yy);
8013 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8015 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8016 RemoveMovingField(xx, yy);
8018 RemoveField(xx, yy);
8020 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8023 RemoveMovingField(xx, yy);
8026 ChangeDelay[xx][yy] = 0;
8028 Feld[xx][yy] = EL_FLAMES;
8030 if (IN_SCR_FIELD(sx, sy))
8032 DrawLevelFieldCrumbledSand(xx, yy);
8033 DrawGraphic(sx, sy, flame_graphic, frame);
8038 if (Feld[xx][yy] == EL_FLAMES)
8039 Feld[xx][yy] = EL_EMPTY;
8040 DrawLevelField(xx, yy);
8045 if (MovDelay[x][y]) /* element still has to wait some time */
8047 PlayLevelSoundAction(x, y, ACTION_WAITING);
8053 /* now make next step */
8055 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8057 if (DONT_COLLIDE_WITH(element) &&
8058 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8059 !PLAYER_ENEMY_PROTECTED(newx, newy))
8061 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8066 else if (CAN_MOVE_INTO_ACID(element) &&
8067 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8068 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8069 (MovDir[x][y] == MV_DOWN ||
8070 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8072 SplashAcid(newx, newy);
8073 Store[x][y] = EL_ACID;
8075 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8077 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8078 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8079 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8080 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8083 DrawLevelField(x, y);
8085 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8086 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8087 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8089 local_player->friends_still_needed--;
8090 if (!local_player->friends_still_needed &&
8091 !local_player->GameOver && AllPlayersGone)
8092 PlayerWins(local_player);
8096 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8098 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8099 DrawLevelField(newx, newy);
8101 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8103 else if (!IS_FREE(newx, newy))
8105 GfxAction[x][y] = ACTION_WAITING;
8107 if (IS_PLAYER(x, y))
8108 DrawPlayerField(x, y);
8110 DrawLevelField(x, y);
8115 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8117 if (IS_FOOD_PIG(Feld[newx][newy]))
8119 if (IS_MOVING(newx, newy))
8120 RemoveMovingField(newx, newy);
8123 Feld[newx][newy] = EL_EMPTY;
8124 DrawLevelField(newx, newy);
8127 PlayLevelSound(x, y, SND_PIG_DIGGING);
8129 else if (!IS_FREE(newx, newy))
8131 if (IS_PLAYER(x, y))
8132 DrawPlayerField(x, y);
8134 DrawLevelField(x, y);
8139 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8141 if (Store[x][y] != EL_EMPTY)
8143 boolean can_clone = FALSE;
8146 /* check if element to clone is still there */
8147 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8149 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8157 /* cannot clone or target field not free anymore -- do not clone */
8158 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8159 Store[x][y] = EL_EMPTY;
8162 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8164 if (IS_MV_DIAGONAL(MovDir[x][y]))
8166 int diagonal_move_dir = MovDir[x][y];
8167 int stored = Store[x][y];
8168 int change_delay = 8;
8171 /* android is moving diagonally */
8173 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8175 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8176 GfxElement[x][y] = EL_EMC_ANDROID;
8177 GfxAction[x][y] = ACTION_SHRINKING;
8178 GfxDir[x][y] = diagonal_move_dir;
8179 ChangeDelay[x][y] = change_delay;
8181 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8184 DrawLevelGraphicAnimation(x, y, graphic);
8185 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8187 if (Feld[newx][newy] == EL_ACID)
8189 SplashAcid(newx, newy);
8194 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8196 Store[newx][newy] = EL_EMC_ANDROID;
8197 GfxElement[newx][newy] = EL_EMC_ANDROID;
8198 GfxAction[newx][newy] = ACTION_GROWING;
8199 GfxDir[newx][newy] = diagonal_move_dir;
8200 ChangeDelay[newx][newy] = change_delay;
8202 graphic = el_act_dir2img(GfxElement[newx][newy],
8203 GfxAction[newx][newy], GfxDir[newx][newy]);
8205 DrawLevelGraphicAnimation(newx, newy, graphic);
8206 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8212 Feld[newx][newy] = EL_EMPTY;
8213 DrawLevelField(newx, newy);
8215 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8218 else if (!IS_FREE(newx, newy))
8221 if (IS_PLAYER(x, y))
8222 DrawPlayerField(x, y);
8224 DrawLevelField(x, y);
8230 else if (IS_CUSTOM_ELEMENT(element) &&
8231 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8233 int new_element = Feld[newx][newy];
8235 if (!IS_FREE(newx, newy))
8237 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8238 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8241 /* no element can dig solid indestructible elements */
8242 if (IS_INDESTRUCTIBLE(new_element) &&
8243 !IS_DIGGABLE(new_element) &&
8244 !IS_COLLECTIBLE(new_element))
8247 if (AmoebaNr[newx][newy] &&
8248 (new_element == EL_AMOEBA_FULL ||
8249 new_element == EL_BD_AMOEBA ||
8250 new_element == EL_AMOEBA_GROWING))
8252 AmoebaCnt[AmoebaNr[newx][newy]]--;
8253 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8256 if (IS_MOVING(newx, newy))
8257 RemoveMovingField(newx, newy);
8260 RemoveField(newx, newy);
8261 DrawLevelField(newx, newy);
8264 /* if digged element was about to explode, prevent the explosion */
8265 ExplodeField[newx][newy] = EX_TYPE_NONE;
8267 PlayLevelSoundAction(x, y, action);
8270 Store[newx][newy] = EL_EMPTY;
8272 /* this makes it possible to leave the removed element again */
8273 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8274 Store[newx][newy] = new_element;
8276 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8278 int move_leave_element = element_info[element].move_leave_element;
8280 /* this makes it possible to leave the removed element again */
8281 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8282 new_element : move_leave_element);
8286 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8288 RunnerVisit[x][y] = FrameCounter;
8289 PlayerVisit[x][y] /= 8; /* expire player visit path */
8292 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8294 if (!IS_FREE(newx, newy))
8296 if (IS_PLAYER(x, y))
8297 DrawPlayerField(x, y);
8299 DrawLevelField(x, y);
8305 boolean wanna_flame = !RND(10);
8306 int dx = newx - x, dy = newy - y;
8307 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8308 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8309 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8310 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8311 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8312 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8315 IS_CLASSIC_ENEMY(element1) ||
8316 IS_CLASSIC_ENEMY(element2)) &&
8317 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8318 element1 != EL_FLAMES && element2 != EL_FLAMES)
8320 ResetGfxAnimation(x, y);
8321 GfxAction[x][y] = ACTION_ATTACKING;
8323 if (IS_PLAYER(x, y))
8324 DrawPlayerField(x, y);
8326 DrawLevelField(x, y);
8328 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8330 MovDelay[x][y] = 50;
8334 RemoveField(newx, newy);
8336 Feld[newx][newy] = EL_FLAMES;
8337 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8340 RemoveField(newx1, newy1);
8342 Feld[newx1][newy1] = EL_FLAMES;
8344 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8347 RemoveField(newx2, newy2);
8349 Feld[newx2][newy2] = EL_FLAMES;
8356 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8357 Feld[newx][newy] == EL_DIAMOND)
8359 if (IS_MOVING(newx, newy))
8360 RemoveMovingField(newx, newy);
8363 Feld[newx][newy] = EL_EMPTY;
8364 DrawLevelField(newx, newy);
8367 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8369 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8370 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8372 if (AmoebaNr[newx][newy])
8374 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8375 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8376 Feld[newx][newy] == EL_BD_AMOEBA)
8377 AmoebaCnt[AmoebaNr[newx][newy]]--;
8382 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8384 RemoveMovingField(newx, newy);
8387 if (IS_MOVING(newx, newy))
8389 RemoveMovingField(newx, newy);
8394 Feld[newx][newy] = EL_EMPTY;
8395 DrawLevelField(newx, newy);
8398 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8400 else if ((element == EL_PACMAN || element == EL_MOLE)
8401 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8403 if (AmoebaNr[newx][newy])
8405 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8406 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8407 Feld[newx][newy] == EL_BD_AMOEBA)
8408 AmoebaCnt[AmoebaNr[newx][newy]]--;
8411 if (element == EL_MOLE)
8413 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8414 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8416 ResetGfxAnimation(x, y);
8417 GfxAction[x][y] = ACTION_DIGGING;
8418 DrawLevelField(x, y);
8420 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8422 return; /* wait for shrinking amoeba */
8424 else /* element == EL_PACMAN */
8426 Feld[newx][newy] = EL_EMPTY;
8427 DrawLevelField(newx, newy);
8428 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8431 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8432 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8433 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8435 /* wait for shrinking amoeba to completely disappear */
8438 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8440 /* object was running against a wall */
8445 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8446 if (move_pattern & MV_ANY_DIRECTION &&
8447 move_pattern == MovDir[x][y])
8449 int blocking_element =
8450 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8452 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8455 element = Feld[x][y]; /* element might have changed */
8459 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8460 DrawLevelElementAnimation(x, y, element);
8462 if (DONT_TOUCH(element))
8463 TestIfBadThingTouchesPlayer(x, y);
8468 InitMovingField(x, y, MovDir[x][y]);
8470 PlayLevelSoundAction(x, y, ACTION_MOVING);
8474 ContinueMoving(x, y);
8477 void ContinueMoving(int x, int y)
8479 int element = Feld[x][y];
8480 struct ElementInfo *ei = &element_info[element];
8481 int direction = MovDir[x][y];
8482 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8483 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8484 int newx = x + dx, newy = y + dy;
8485 int stored = Store[x][y];
8486 int stored_new = Store[newx][newy];
8487 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8488 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8489 boolean last_line = (newy == lev_fieldy - 1);
8491 MovPos[x][y] += getElementMoveStepsize(x, y);
8493 if (pushed_by_player) /* special case: moving object pushed by player */
8494 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8496 if (ABS(MovPos[x][y]) < TILEX)
8499 int ee = Feld[x][y];
8500 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8501 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8503 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8504 x, y, ABS(MovPos[x][y]),
8506 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8509 DrawLevelField(x, y);
8511 return; /* element is still moving */
8514 /* element reached destination field */
8516 Feld[x][y] = EL_EMPTY;
8517 Feld[newx][newy] = element;
8518 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8520 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8522 element = Feld[newx][newy] = EL_ACID;
8524 else if (element == EL_MOLE)
8526 Feld[x][y] = EL_SAND;
8528 DrawLevelFieldCrumbledSandNeighbours(x, y);
8530 else if (element == EL_QUICKSAND_FILLING)
8532 element = Feld[newx][newy] = get_next_element(element);
8533 Store[newx][newy] = Store[x][y];
8535 else if (element == EL_QUICKSAND_EMPTYING)
8537 Feld[x][y] = get_next_element(element);
8538 element = Feld[newx][newy] = Store[x][y];
8540 else if (element == EL_QUICKSAND_FAST_FILLING)
8542 element = Feld[newx][newy] = get_next_element(element);
8543 Store[newx][newy] = Store[x][y];
8545 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8547 Feld[x][y] = get_next_element(element);
8548 element = Feld[newx][newy] = Store[x][y];
8550 else if (element == EL_MAGIC_WALL_FILLING)
8552 element = Feld[newx][newy] = get_next_element(element);
8553 if (!game.magic_wall_active)
8554 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8555 Store[newx][newy] = Store[x][y];
8557 else if (element == EL_MAGIC_WALL_EMPTYING)
8559 Feld[x][y] = get_next_element(element);
8560 if (!game.magic_wall_active)
8561 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8562 element = Feld[newx][newy] = Store[x][y];
8564 #if USE_NEW_CUSTOM_VALUE
8565 InitField(newx, newy, FALSE);
8568 else if (element == EL_BD_MAGIC_WALL_FILLING)
8570 element = Feld[newx][newy] = get_next_element(element);
8571 if (!game.magic_wall_active)
8572 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8573 Store[newx][newy] = Store[x][y];
8575 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8577 Feld[x][y] = get_next_element(element);
8578 if (!game.magic_wall_active)
8579 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8580 element = Feld[newx][newy] = Store[x][y];
8582 #if USE_NEW_CUSTOM_VALUE
8583 InitField(newx, newy, FALSE);
8586 else if (element == EL_DC_MAGIC_WALL_FILLING)
8588 element = Feld[newx][newy] = get_next_element(element);
8589 if (!game.magic_wall_active)
8590 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8591 Store[newx][newy] = Store[x][y];
8593 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8595 Feld[x][y] = get_next_element(element);
8596 if (!game.magic_wall_active)
8597 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8598 element = Feld[newx][newy] = Store[x][y];
8600 #if USE_NEW_CUSTOM_VALUE
8601 InitField(newx, newy, FALSE);
8604 else if (element == EL_AMOEBA_DROPPING)
8606 Feld[x][y] = get_next_element(element);
8607 element = Feld[newx][newy] = Store[x][y];
8609 else if (element == EL_SOKOBAN_OBJECT)
8612 Feld[x][y] = Back[x][y];
8614 if (Back[newx][newy])
8615 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8617 Back[x][y] = Back[newx][newy] = 0;
8620 Store[x][y] = EL_EMPTY;
8625 MovDelay[newx][newy] = 0;
8627 if (CAN_CHANGE_OR_HAS_ACTION(element))
8629 /* copy element change control values to new field */
8630 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8631 ChangePage[newx][newy] = ChangePage[x][y];
8632 ChangeCount[newx][newy] = ChangeCount[x][y];
8633 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8636 #if USE_NEW_CUSTOM_VALUE
8637 CustomValue[newx][newy] = CustomValue[x][y];
8640 ChangeDelay[x][y] = 0;
8641 ChangePage[x][y] = -1;
8642 ChangeCount[x][y] = 0;
8643 ChangeEvent[x][y] = -1;
8645 #if USE_NEW_CUSTOM_VALUE
8646 CustomValue[x][y] = 0;
8649 /* copy animation control values to new field */
8650 GfxFrame[newx][newy] = GfxFrame[x][y];
8651 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8652 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8653 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8655 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8657 /* some elements can leave other elements behind after moving */
8659 if (ei->move_leave_element != EL_EMPTY &&
8660 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8661 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8663 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8664 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8665 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8668 int move_leave_element = ei->move_leave_element;
8672 /* this makes it possible to leave the removed element again */
8673 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8674 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8676 /* this makes it possible to leave the removed element again */
8677 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8678 move_leave_element = stored;
8681 /* this makes it possible to leave the removed element again */
8682 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8683 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8684 move_leave_element = stored;
8687 Feld[x][y] = move_leave_element;
8689 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8690 MovDir[x][y] = direction;
8692 InitField(x, y, FALSE);
8694 if (GFX_CRUMBLED(Feld[x][y]))
8695 DrawLevelFieldCrumbledSandNeighbours(x, y);
8697 if (ELEM_IS_PLAYER(move_leave_element))
8698 RelocatePlayer(x, y, move_leave_element);
8701 /* do this after checking for left-behind element */
8702 ResetGfxAnimation(x, y); /* reset animation values for old field */
8704 if (!CAN_MOVE(element) ||
8705 (CAN_FALL(element) && direction == MV_DOWN &&
8706 (element == EL_SPRING ||
8707 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8708 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8709 GfxDir[x][y] = MovDir[newx][newy] = 0;
8711 DrawLevelField(x, y);
8712 DrawLevelField(newx, newy);
8714 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8716 /* prevent pushed element from moving on in pushed direction */
8717 if (pushed_by_player && CAN_MOVE(element) &&
8718 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8719 !(element_info[element].move_pattern & direction))
8720 TurnRound(newx, newy);
8722 /* prevent elements on conveyor belt from moving on in last direction */
8723 if (pushed_by_conveyor && CAN_FALL(element) &&
8724 direction & MV_HORIZONTAL)
8725 MovDir[newx][newy] = 0;
8727 if (!pushed_by_player)
8729 int nextx = newx + dx, nexty = newy + dy;
8730 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8732 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8734 if (CAN_FALL(element) && direction == MV_DOWN)
8735 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8737 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8738 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8740 #if USE_FIX_IMPACT_COLLISION
8741 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8742 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8746 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8748 TestIfBadThingTouchesPlayer(newx, newy);
8749 TestIfBadThingTouchesFriend(newx, newy);
8751 if (!IS_CUSTOM_ELEMENT(element))
8752 TestIfBadThingTouchesOtherBadThing(newx, newy);
8754 else if (element == EL_PENGUIN)
8755 TestIfFriendTouchesBadThing(newx, newy);
8757 /* give the player one last chance (one more frame) to move away */
8758 if (CAN_FALL(element) && direction == MV_DOWN &&
8759 (last_line || (!IS_FREE(x, newy + 1) &&
8760 (!IS_PLAYER(x, newy + 1) ||
8761 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8764 if (pushed_by_player && !game.use_change_when_pushing_bug)
8766 int push_side = MV_DIR_OPPOSITE(direction);
8767 struct PlayerInfo *player = PLAYERINFO(x, y);
8769 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8770 player->index_bit, push_side);
8771 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8772 player->index_bit, push_side);
8775 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8776 MovDelay[newx][newy] = 1;
8778 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8780 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8783 if (ChangePage[newx][newy] != -1) /* delayed change */
8785 int page = ChangePage[newx][newy];
8786 struct ElementChangeInfo *change = &ei->change_page[page];
8788 ChangePage[newx][newy] = -1;
8790 if (change->can_change)
8792 if (ChangeElement(newx, newy, element, page))
8794 if (change->post_change_function)
8795 change->post_change_function(newx, newy);
8799 if (change->has_action)
8800 ExecuteCustomElementAction(newx, newy, element, page);
8804 TestIfElementHitsCustomElement(newx, newy, direction);
8805 TestIfPlayerTouchesCustomElement(newx, newy);
8806 TestIfElementTouchesCustomElement(newx, newy);
8808 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8809 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8810 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8811 MV_DIR_OPPOSITE(direction));
8814 int AmoebeNachbarNr(int ax, int ay)
8817 int element = Feld[ax][ay];
8819 static int xy[4][2] =
8827 for (i = 0; i < NUM_DIRECTIONS; i++)
8829 int x = ax + xy[i][0];
8830 int y = ay + xy[i][1];
8832 if (!IN_LEV_FIELD(x, y))
8835 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8836 group_nr = AmoebaNr[x][y];
8842 void AmoebenVereinigen(int ax, int ay)
8844 int i, x, y, xx, yy;
8845 int new_group_nr = AmoebaNr[ax][ay];
8846 static int xy[4][2] =
8854 if (new_group_nr == 0)
8857 for (i = 0; i < NUM_DIRECTIONS; i++)
8862 if (!IN_LEV_FIELD(x, y))
8865 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8866 Feld[x][y] == EL_BD_AMOEBA ||
8867 Feld[x][y] == EL_AMOEBA_DEAD) &&
8868 AmoebaNr[x][y] != new_group_nr)
8870 int old_group_nr = AmoebaNr[x][y];
8872 if (old_group_nr == 0)
8875 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8876 AmoebaCnt[old_group_nr] = 0;
8877 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8878 AmoebaCnt2[old_group_nr] = 0;
8880 SCAN_PLAYFIELD(xx, yy)
8882 if (AmoebaNr[xx][yy] == old_group_nr)
8883 AmoebaNr[xx][yy] = new_group_nr;
8889 void AmoebeUmwandeln(int ax, int ay)
8893 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8895 int group_nr = AmoebaNr[ax][ay];
8900 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8901 printf("AmoebeUmwandeln(): This should never happen!\n");
8906 SCAN_PLAYFIELD(x, y)
8908 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8911 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8915 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8916 SND_AMOEBA_TURNING_TO_GEM :
8917 SND_AMOEBA_TURNING_TO_ROCK));
8922 static int xy[4][2] =
8930 for (i = 0; i < NUM_DIRECTIONS; i++)
8935 if (!IN_LEV_FIELD(x, y))
8938 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8940 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8941 SND_AMOEBA_TURNING_TO_GEM :
8942 SND_AMOEBA_TURNING_TO_ROCK));
8949 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8952 int group_nr = AmoebaNr[ax][ay];
8953 boolean done = FALSE;
8958 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8959 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8964 SCAN_PLAYFIELD(x, y)
8966 if (AmoebaNr[x][y] == group_nr &&
8967 (Feld[x][y] == EL_AMOEBA_DEAD ||
8968 Feld[x][y] == EL_BD_AMOEBA ||
8969 Feld[x][y] == EL_AMOEBA_GROWING))
8972 Feld[x][y] = new_element;
8973 InitField(x, y, FALSE);
8974 DrawLevelField(x, y);
8980 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8981 SND_BD_AMOEBA_TURNING_TO_ROCK :
8982 SND_BD_AMOEBA_TURNING_TO_GEM));
8985 void AmoebeWaechst(int x, int y)
8987 static unsigned long sound_delay = 0;
8988 static unsigned long sound_delay_value = 0;
8990 if (!MovDelay[x][y]) /* start new growing cycle */
8994 if (DelayReached(&sound_delay, sound_delay_value))
8996 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8997 sound_delay_value = 30;
9001 if (MovDelay[x][y]) /* wait some time before growing bigger */
9004 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9006 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9007 6 - MovDelay[x][y]);
9009 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9012 if (!MovDelay[x][y])
9014 Feld[x][y] = Store[x][y];
9016 DrawLevelField(x, y);
9021 void AmoebaDisappearing(int x, int y)
9023 static unsigned long sound_delay = 0;
9024 static unsigned long sound_delay_value = 0;
9026 if (!MovDelay[x][y]) /* start new shrinking cycle */
9030 if (DelayReached(&sound_delay, sound_delay_value))
9031 sound_delay_value = 30;
9034 if (MovDelay[x][y]) /* wait some time before shrinking */
9037 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9039 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9040 6 - MovDelay[x][y]);
9042 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9045 if (!MovDelay[x][y])
9047 Feld[x][y] = EL_EMPTY;
9048 DrawLevelField(x, y);
9050 /* don't let mole enter this field in this cycle;
9051 (give priority to objects falling to this field from above) */
9057 void AmoebeAbleger(int ax, int ay)
9060 int element = Feld[ax][ay];
9061 int graphic = el2img(element);
9062 int newax = ax, neway = ay;
9063 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9064 static int xy[4][2] =
9072 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9074 Feld[ax][ay] = EL_AMOEBA_DEAD;
9075 DrawLevelField(ax, ay);
9079 if (IS_ANIMATED(graphic))
9080 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9082 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9083 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9085 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9088 if (MovDelay[ax][ay])
9092 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9095 int x = ax + xy[start][0];
9096 int y = ay + xy[start][1];
9098 if (!IN_LEV_FIELD(x, y))
9101 if (IS_FREE(x, y) ||
9102 CAN_GROW_INTO(Feld[x][y]) ||
9103 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9104 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9110 if (newax == ax && neway == ay)
9113 else /* normal or "filled" (BD style) amoeba */
9116 boolean waiting_for_player = FALSE;
9118 for (i = 0; i < NUM_DIRECTIONS; i++)
9120 int j = (start + i) % 4;
9121 int x = ax + xy[j][0];
9122 int y = ay + xy[j][1];
9124 if (!IN_LEV_FIELD(x, y))
9127 if (IS_FREE(x, y) ||
9128 CAN_GROW_INTO(Feld[x][y]) ||
9129 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9130 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9136 else if (IS_PLAYER(x, y))
9137 waiting_for_player = TRUE;
9140 if (newax == ax && neway == ay) /* amoeba cannot grow */
9142 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9144 Feld[ax][ay] = EL_AMOEBA_DEAD;
9145 DrawLevelField(ax, ay);
9146 AmoebaCnt[AmoebaNr[ax][ay]]--;
9148 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9150 if (element == EL_AMOEBA_FULL)
9151 AmoebeUmwandeln(ax, ay);
9152 else if (element == EL_BD_AMOEBA)
9153 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9158 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9160 /* amoeba gets larger by growing in some direction */
9162 int new_group_nr = AmoebaNr[ax][ay];
9165 if (new_group_nr == 0)
9167 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9168 printf("AmoebeAbleger(): This should never happen!\n");
9173 AmoebaNr[newax][neway] = new_group_nr;
9174 AmoebaCnt[new_group_nr]++;
9175 AmoebaCnt2[new_group_nr]++;
9177 /* if amoeba touches other amoeba(s) after growing, unify them */
9178 AmoebenVereinigen(newax, neway);
9180 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9182 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9188 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9189 (neway == lev_fieldy - 1 && newax != ax))
9191 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9192 Store[newax][neway] = element;
9194 else if (neway == ay || element == EL_EMC_DRIPPER)
9196 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9198 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9202 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9203 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9204 Store[ax][ay] = EL_AMOEBA_DROP;
9205 ContinueMoving(ax, ay);
9209 DrawLevelField(newax, neway);
9212 void Life(int ax, int ay)
9216 int element = Feld[ax][ay];
9217 int graphic = el2img(element);
9218 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9220 boolean changed = FALSE;
9222 if (IS_ANIMATED(graphic))
9223 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9228 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9229 MovDelay[ax][ay] = life_time;
9231 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9234 if (MovDelay[ax][ay])
9238 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9240 int xx = ax+x1, yy = ay+y1;
9243 if (!IN_LEV_FIELD(xx, yy))
9246 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9248 int x = xx+x2, y = yy+y2;
9250 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9253 if (((Feld[x][y] == element ||
9254 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9256 (IS_FREE(x, y) && Stop[x][y]))
9260 if (xx == ax && yy == ay) /* field in the middle */
9262 if (nachbarn < life_parameter[0] ||
9263 nachbarn > life_parameter[1])
9265 Feld[xx][yy] = EL_EMPTY;
9267 DrawLevelField(xx, yy);
9268 Stop[xx][yy] = TRUE;
9272 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9273 { /* free border field */
9274 if (nachbarn >= life_parameter[2] &&
9275 nachbarn <= life_parameter[3])
9277 Feld[xx][yy] = element;
9278 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9280 DrawLevelField(xx, yy);
9281 Stop[xx][yy] = TRUE;
9288 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9289 SND_GAME_OF_LIFE_GROWING);
9292 static void InitRobotWheel(int x, int y)
9294 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9297 static void RunRobotWheel(int x, int y)
9299 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9302 static void StopRobotWheel(int x, int y)
9304 if (ZX == x && ZY == y)
9308 game.robot_wheel_active = FALSE;
9312 static void InitTimegateWheel(int x, int y)
9314 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9317 static void RunTimegateWheel(int x, int y)
9319 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9322 static void InitMagicBallDelay(int x, int y)
9325 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9327 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9331 static void ActivateMagicBall(int bx, int by)
9335 if (level.ball_random)
9337 int pos_border = RND(8); /* select one of the eight border elements */
9338 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9339 int xx = pos_content % 3;
9340 int yy = pos_content / 3;
9345 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9346 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9350 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9352 int xx = x - bx + 1;
9353 int yy = y - by + 1;
9355 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9356 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9360 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9363 void CheckExit(int x, int y)
9365 if (local_player->gems_still_needed > 0 ||
9366 local_player->sokobanfields_still_needed > 0 ||
9367 local_player->lights_still_needed > 0)
9369 int element = Feld[x][y];
9370 int graphic = el2img(element);
9372 if (IS_ANIMATED(graphic))
9373 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9378 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9381 Feld[x][y] = EL_EXIT_OPENING;
9383 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9386 void CheckExitEM(int x, int y)
9388 if (local_player->gems_still_needed > 0 ||
9389 local_player->sokobanfields_still_needed > 0 ||
9390 local_player->lights_still_needed > 0)
9392 int element = Feld[x][y];
9393 int graphic = el2img(element);
9395 if (IS_ANIMATED(graphic))
9396 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9401 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9404 Feld[x][y] = EL_EM_EXIT_OPENING;
9406 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9409 void CheckExitSteel(int x, int y)
9411 if (local_player->gems_still_needed > 0 ||
9412 local_player->sokobanfields_still_needed > 0 ||
9413 local_player->lights_still_needed > 0)
9415 int element = Feld[x][y];
9416 int graphic = el2img(element);
9418 if (IS_ANIMATED(graphic))
9419 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9424 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9427 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9429 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9432 void CheckExitSteelEM(int x, int y)
9434 if (local_player->gems_still_needed > 0 ||
9435 local_player->sokobanfields_still_needed > 0 ||
9436 local_player->lights_still_needed > 0)
9438 int element = Feld[x][y];
9439 int graphic = el2img(element);
9441 if (IS_ANIMATED(graphic))
9442 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9447 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9450 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9452 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9455 void CheckExitSP(int x, int y)
9457 if (local_player->gems_still_needed > 0)
9459 int element = Feld[x][y];
9460 int graphic = el2img(element);
9462 if (IS_ANIMATED(graphic))
9463 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9468 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9471 Feld[x][y] = EL_SP_EXIT_OPENING;
9473 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9476 static void CloseAllOpenTimegates()
9480 SCAN_PLAYFIELD(x, y)
9482 int element = Feld[x][y];
9484 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9486 Feld[x][y] = EL_TIMEGATE_CLOSING;
9488 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9493 void DrawTwinkleOnField(int x, int y)
9495 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9498 if (Feld[x][y] == EL_BD_DIAMOND)
9501 if (MovDelay[x][y] == 0) /* next animation frame */
9502 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9504 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9508 DrawLevelElementAnimation(x, y, Feld[x][y]);
9510 if (MovDelay[x][y] != 0)
9512 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9513 10 - MovDelay[x][y]);
9515 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9520 void MauerWaechst(int x, int y)
9524 if (!MovDelay[x][y]) /* next animation frame */
9525 MovDelay[x][y] = 3 * delay;
9527 if (MovDelay[x][y]) /* wait some time before next frame */
9531 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9533 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9534 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9536 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9539 if (!MovDelay[x][y])
9541 if (MovDir[x][y] == MV_LEFT)
9543 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9544 DrawLevelField(x - 1, y);
9546 else if (MovDir[x][y] == MV_RIGHT)
9548 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9549 DrawLevelField(x + 1, y);
9551 else if (MovDir[x][y] == MV_UP)
9553 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9554 DrawLevelField(x, y - 1);
9558 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9559 DrawLevelField(x, y + 1);
9562 Feld[x][y] = Store[x][y];
9564 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9565 DrawLevelField(x, y);
9570 void MauerAbleger(int ax, int ay)
9572 int element = Feld[ax][ay];
9573 int graphic = el2img(element);
9574 boolean oben_frei = FALSE, unten_frei = FALSE;
9575 boolean links_frei = FALSE, rechts_frei = FALSE;
9576 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9577 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9578 boolean new_wall = FALSE;
9580 if (IS_ANIMATED(graphic))
9581 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583 if (!MovDelay[ax][ay]) /* start building new wall */
9584 MovDelay[ax][ay] = 6;
9586 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9589 if (MovDelay[ax][ay])
9593 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9595 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9597 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9599 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9602 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9603 element == EL_EXPANDABLE_WALL_ANY)
9607 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9608 Store[ax][ay-1] = element;
9609 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9610 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9611 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9612 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9617 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9618 Store[ax][ay+1] = element;
9619 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9620 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9621 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9622 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9627 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9628 element == EL_EXPANDABLE_WALL_ANY ||
9629 element == EL_EXPANDABLE_WALL ||
9630 element == EL_BD_EXPANDABLE_WALL)
9634 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9635 Store[ax-1][ay] = element;
9636 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9637 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9638 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9639 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9645 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9646 Store[ax+1][ay] = element;
9647 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9648 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9649 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9650 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9655 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9656 DrawLevelField(ax, ay);
9658 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9660 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9661 unten_massiv = TRUE;
9662 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9663 links_massiv = TRUE;
9664 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9665 rechts_massiv = TRUE;
9667 if (((oben_massiv && unten_massiv) ||
9668 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9669 element == EL_EXPANDABLE_WALL) &&
9670 ((links_massiv && rechts_massiv) ||
9671 element == EL_EXPANDABLE_WALL_VERTICAL))
9672 Feld[ax][ay] = EL_WALL;
9675 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9678 void MauerAblegerStahl(int ax, int ay)
9680 int element = Feld[ax][ay];
9681 int graphic = el2img(element);
9682 boolean oben_frei = FALSE, unten_frei = FALSE;
9683 boolean links_frei = FALSE, rechts_frei = FALSE;
9684 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9685 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9686 boolean new_wall = FALSE;
9688 if (IS_ANIMATED(graphic))
9689 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9691 if (!MovDelay[ax][ay]) /* start building new wall */
9692 MovDelay[ax][ay] = 6;
9694 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9697 if (MovDelay[ax][ay])
9701 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9703 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9705 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9707 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9710 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9711 element == EL_EXPANDABLE_STEELWALL_ANY)
9715 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9716 Store[ax][ay-1] = element;
9717 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9718 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9719 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9720 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9725 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9726 Store[ax][ay+1] = element;
9727 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9728 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9729 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9730 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9735 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9736 element == EL_EXPANDABLE_STEELWALL_ANY)
9740 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9741 Store[ax-1][ay] = element;
9742 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9743 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9744 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9745 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9751 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9752 Store[ax+1][ay] = element;
9753 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9754 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9755 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9756 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9761 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9763 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9764 unten_massiv = TRUE;
9765 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9766 links_massiv = TRUE;
9767 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9768 rechts_massiv = TRUE;
9770 if (((oben_massiv && unten_massiv) ||
9771 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9772 ((links_massiv && rechts_massiv) ||
9773 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9774 Feld[ax][ay] = EL_WALL;
9777 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9780 void CheckForDragon(int x, int y)
9783 boolean dragon_found = FALSE;
9784 static int xy[4][2] =
9792 for (i = 0; i < NUM_DIRECTIONS; i++)
9794 for (j = 0; j < 4; j++)
9796 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9798 if (IN_LEV_FIELD(xx, yy) &&
9799 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9801 if (Feld[xx][yy] == EL_DRAGON)
9802 dragon_found = TRUE;
9811 for (i = 0; i < NUM_DIRECTIONS; i++)
9813 for (j = 0; j < 3; j++)
9815 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9817 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9819 Feld[xx][yy] = EL_EMPTY;
9820 DrawLevelField(xx, yy);
9829 static void InitBuggyBase(int x, int y)
9831 int element = Feld[x][y];
9832 int activating_delay = FRAMES_PER_SECOND / 4;
9835 (element == EL_SP_BUGGY_BASE ?
9836 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9837 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9839 element == EL_SP_BUGGY_BASE_ACTIVE ?
9840 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9843 static void WarnBuggyBase(int x, int y)
9846 static int xy[4][2] =
9854 for (i = 0; i < NUM_DIRECTIONS; i++)
9856 int xx = x + xy[i][0];
9857 int yy = y + xy[i][1];
9859 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9861 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9868 static void InitTrap(int x, int y)
9870 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9873 static void ActivateTrap(int x, int y)
9875 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9878 static void ChangeActiveTrap(int x, int y)
9880 int graphic = IMG_TRAP_ACTIVE;
9882 /* if new animation frame was drawn, correct crumbled sand border */
9883 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9884 DrawLevelFieldCrumbledSand(x, y);
9887 static int getSpecialActionElement(int element, int number, int base_element)
9889 return (element != EL_EMPTY ? element :
9890 number != -1 ? base_element + number - 1 :
9894 static int getModifiedActionNumber(int value_old, int operator, int operand,
9895 int value_min, int value_max)
9897 int value_new = (operator == CA_MODE_SET ? operand :
9898 operator == CA_MODE_ADD ? value_old + operand :
9899 operator == CA_MODE_SUBTRACT ? value_old - operand :
9900 operator == CA_MODE_MULTIPLY ? value_old * operand :
9901 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9902 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9905 return (value_new < value_min ? value_min :
9906 value_new > value_max ? value_max :
9910 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9912 struct ElementInfo *ei = &element_info[element];
9913 struct ElementChangeInfo *change = &ei->change_page[page];
9914 int target_element = change->target_element;
9915 int action_type = change->action_type;
9916 int action_mode = change->action_mode;
9917 int action_arg = change->action_arg;
9920 if (!change->has_action)
9923 /* ---------- determine action paramater values -------------------------- */
9925 int level_time_value =
9926 (level.time > 0 ? TimeLeft :
9929 int action_arg_element =
9930 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9931 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9932 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9935 int action_arg_direction =
9936 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9937 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9938 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9939 change->actual_trigger_side :
9940 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9941 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9944 int action_arg_number_min =
9945 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9948 int action_arg_number_max =
9949 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9950 action_type == CA_SET_LEVEL_GEMS ? 999 :
9951 action_type == CA_SET_LEVEL_TIME ? 9999 :
9952 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9953 action_type == CA_SET_CE_VALUE ? 9999 :
9954 action_type == CA_SET_CE_SCORE ? 9999 :
9957 int action_arg_number_reset =
9958 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9959 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9960 action_type == CA_SET_LEVEL_TIME ? level.time :
9961 action_type == CA_SET_LEVEL_SCORE ? 0 :
9962 #if USE_NEW_CUSTOM_VALUE
9963 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9965 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9967 action_type == CA_SET_CE_SCORE ? 0 :
9970 int action_arg_number =
9971 (action_arg <= CA_ARG_MAX ? action_arg :
9972 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9973 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9974 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9975 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9976 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9977 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9978 #if USE_NEW_CUSTOM_VALUE
9979 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9981 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9983 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9984 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9985 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9986 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9987 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9988 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9989 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9990 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9991 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9992 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
9993 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9996 int action_arg_number_old =
9997 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9998 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9999 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10000 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10001 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10004 int action_arg_number_new =
10005 getModifiedActionNumber(action_arg_number_old,
10006 action_mode, action_arg_number,
10007 action_arg_number_min, action_arg_number_max);
10010 int trigger_player_bits = change->actual_trigger_player_bits;
10012 int trigger_player_bits =
10013 (change->actual_trigger_player >= EL_PLAYER_1 &&
10014 change->actual_trigger_player <= EL_PLAYER_4 ?
10015 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10019 int action_arg_player_bits =
10020 (action_arg >= CA_ARG_PLAYER_1 &&
10021 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10022 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10025 /* ---------- execute action -------------------------------------------- */
10027 switch (action_type)
10034 /* ---------- level actions ------------------------------------------- */
10036 case CA_RESTART_LEVEL:
10038 game.restart_level = TRUE;
10043 case CA_SHOW_ENVELOPE:
10045 int element = getSpecialActionElement(action_arg_element,
10046 action_arg_number, EL_ENVELOPE_1);
10048 if (IS_ENVELOPE(element))
10049 local_player->show_envelope = element;
10054 case CA_SET_LEVEL_TIME:
10056 if (level.time > 0) /* only modify limited time value */
10058 TimeLeft = action_arg_number_new;
10061 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10063 DisplayGameControlValues();
10065 DrawGameValue_Time(TimeLeft);
10068 if (!TimeLeft && setup.time_limit)
10069 for (i = 0; i < MAX_PLAYERS; i++)
10070 KillPlayer(&stored_player[i]);
10076 case CA_SET_LEVEL_SCORE:
10078 local_player->score = action_arg_number_new;
10081 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10083 DisplayGameControlValues();
10085 DrawGameValue_Score(local_player->score);
10091 case CA_SET_LEVEL_GEMS:
10093 local_player->gems_still_needed = action_arg_number_new;
10096 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10098 DisplayGameControlValues();
10100 DrawGameValue_Emeralds(local_player->gems_still_needed);
10106 #if !USE_PLAYER_GRAVITY
10107 case CA_SET_LEVEL_GRAVITY:
10109 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10110 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10111 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10117 case CA_SET_LEVEL_WIND:
10119 game.wind_direction = action_arg_direction;
10124 /* ---------- player actions ------------------------------------------ */
10126 case CA_MOVE_PLAYER:
10128 /* automatically move to the next field in specified direction */
10129 for (i = 0; i < MAX_PLAYERS; i++)
10130 if (trigger_player_bits & (1 << i))
10131 stored_player[i].programmed_action = action_arg_direction;
10136 case CA_EXIT_PLAYER:
10138 for (i = 0; i < MAX_PLAYERS; i++)
10139 if (action_arg_player_bits & (1 << i))
10140 PlayerWins(&stored_player[i]);
10145 case CA_KILL_PLAYER:
10147 for (i = 0; i < MAX_PLAYERS; i++)
10148 if (action_arg_player_bits & (1 << i))
10149 KillPlayer(&stored_player[i]);
10154 case CA_SET_PLAYER_KEYS:
10156 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10157 int element = getSpecialActionElement(action_arg_element,
10158 action_arg_number, EL_KEY_1);
10160 if (IS_KEY(element))
10162 for (i = 0; i < MAX_PLAYERS; i++)
10164 if (trigger_player_bits & (1 << i))
10166 stored_player[i].key[KEY_NR(element)] = key_state;
10168 DrawGameDoorValues();
10176 case CA_SET_PLAYER_SPEED:
10178 for (i = 0; i < MAX_PLAYERS; i++)
10180 if (trigger_player_bits & (1 << i))
10182 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10184 if (action_arg == CA_ARG_SPEED_FASTER &&
10185 stored_player[i].cannot_move)
10187 action_arg_number = STEPSIZE_VERY_SLOW;
10189 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10190 action_arg == CA_ARG_SPEED_FASTER)
10192 action_arg_number = 2;
10193 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10196 else if (action_arg == CA_ARG_NUMBER_RESET)
10198 action_arg_number = level.initial_player_stepsize[i];
10202 getModifiedActionNumber(move_stepsize,
10205 action_arg_number_min,
10206 action_arg_number_max);
10208 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10215 case CA_SET_PLAYER_SHIELD:
10217 for (i = 0; i < MAX_PLAYERS; i++)
10219 if (trigger_player_bits & (1 << i))
10221 if (action_arg == CA_ARG_SHIELD_OFF)
10223 stored_player[i].shield_normal_time_left = 0;
10224 stored_player[i].shield_deadly_time_left = 0;
10226 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10228 stored_player[i].shield_normal_time_left = 999999;
10230 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10232 stored_player[i].shield_normal_time_left = 999999;
10233 stored_player[i].shield_deadly_time_left = 999999;
10241 #if USE_PLAYER_GRAVITY
10242 case CA_SET_PLAYER_GRAVITY:
10244 for (i = 0; i < MAX_PLAYERS; i++)
10246 if (trigger_player_bits & (1 << i))
10248 stored_player[i].gravity =
10249 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10250 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10251 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10252 stored_player[i].gravity);
10260 case CA_SET_PLAYER_ARTWORK:
10262 for (i = 0; i < MAX_PLAYERS; i++)
10264 if (trigger_player_bits & (1 << i))
10266 int artwork_element = action_arg_element;
10268 if (action_arg == CA_ARG_ELEMENT_RESET)
10270 (level.use_artwork_element[i] ? level.artwork_element[i] :
10271 stored_player[i].element_nr);
10273 #if USE_GFX_RESET_PLAYER_ARTWORK
10274 if (stored_player[i].artwork_element != artwork_element)
10275 stored_player[i].Frame = 0;
10278 stored_player[i].artwork_element = artwork_element;
10280 SetPlayerWaiting(&stored_player[i], FALSE);
10282 /* set number of special actions for bored and sleeping animation */
10283 stored_player[i].num_special_action_bored =
10284 get_num_special_action(artwork_element,
10285 ACTION_BORING_1, ACTION_BORING_LAST);
10286 stored_player[i].num_special_action_sleeping =
10287 get_num_special_action(artwork_element,
10288 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10295 /* ---------- CE actions ---------------------------------------------- */
10297 case CA_SET_CE_VALUE:
10299 #if USE_NEW_CUSTOM_VALUE
10300 int last_ce_value = CustomValue[x][y];
10302 CustomValue[x][y] = action_arg_number_new;
10304 if (CustomValue[x][y] != last_ce_value)
10306 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10307 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10309 if (CustomValue[x][y] == 0)
10311 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10312 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10320 case CA_SET_CE_SCORE:
10322 #if USE_NEW_CUSTOM_VALUE
10323 int last_ce_score = ei->collect_score;
10325 ei->collect_score = action_arg_number_new;
10327 if (ei->collect_score != last_ce_score)
10329 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10330 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10332 if (ei->collect_score == 0)
10336 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10337 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10340 This is a very special case that seems to be a mixture between
10341 CheckElementChange() and CheckTriggeredElementChange(): while
10342 the first one only affects single elements that are triggered
10343 directly, the second one affects multiple elements in the playfield
10344 that are triggered indirectly by another element. This is a third
10345 case: Changing the CE score always affects multiple identical CEs,
10346 so every affected CE must be checked, not only the single CE for
10347 which the CE score was changed in the first place (as every instance
10348 of that CE shares the same CE score, and therefore also can change)!
10350 SCAN_PLAYFIELD(xx, yy)
10352 if (Feld[xx][yy] == element)
10353 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10354 CE_SCORE_GETS_ZERO);
10363 /* ---------- engine actions ------------------------------------------ */
10365 case CA_SET_ENGINE_SCAN_MODE:
10367 InitPlayfieldScanMode(action_arg);
10377 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10379 int old_element = Feld[x][y];
10380 int new_element = GetElementFromGroupElement(element);
10381 int previous_move_direction = MovDir[x][y];
10382 #if USE_NEW_CUSTOM_VALUE
10383 int last_ce_value = CustomValue[x][y];
10385 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10386 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10387 boolean add_player_onto_element = (new_element_is_player &&
10388 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10389 /* this breaks SnakeBite when a snake is
10390 halfway through a door that closes */
10391 /* NOW FIXED AT LEVEL INIT IN files.c */
10392 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10394 IS_WALKABLE(old_element));
10397 /* check if element under the player changes from accessible to unaccessible
10398 (needed for special case of dropping element which then changes) */
10399 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10400 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10408 if (!add_player_onto_element)
10410 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10411 RemoveMovingField(x, y);
10415 Feld[x][y] = new_element;
10417 #if !USE_GFX_RESET_GFX_ANIMATION
10418 ResetGfxAnimation(x, y);
10419 ResetRandomAnimationValue(x, y);
10422 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10423 MovDir[x][y] = previous_move_direction;
10425 #if USE_NEW_CUSTOM_VALUE
10426 if (element_info[new_element].use_last_ce_value)
10427 CustomValue[x][y] = last_ce_value;
10430 InitField_WithBug1(x, y, FALSE);
10432 new_element = Feld[x][y]; /* element may have changed */
10434 #if USE_GFX_RESET_GFX_ANIMATION
10435 ResetGfxAnimation(x, y);
10436 ResetRandomAnimationValue(x, y);
10439 DrawLevelField(x, y);
10441 if (GFX_CRUMBLED(new_element))
10442 DrawLevelFieldCrumbledSandNeighbours(x, y);
10446 /* check if element under the player changes from accessible to unaccessible
10447 (needed for special case of dropping element which then changes) */
10448 /* (must be checked after creating new element for walkable group elements) */
10449 #if USE_FIX_KILLED_BY_NON_WALKABLE
10450 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10451 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10458 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10459 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10468 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10469 if (new_element_is_player)
10470 RelocatePlayer(x, y, new_element);
10473 ChangeCount[x][y]++; /* count number of changes in the same frame */
10475 TestIfBadThingTouchesPlayer(x, y);
10476 TestIfPlayerTouchesCustomElement(x, y);
10477 TestIfElementTouchesCustomElement(x, y);
10480 static void CreateField(int x, int y, int element)
10482 CreateFieldExt(x, y, element, FALSE);
10485 static void CreateElementFromChange(int x, int y, int element)
10487 element = GET_VALID_RUNTIME_ELEMENT(element);
10489 #if USE_STOP_CHANGED_ELEMENTS
10490 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10492 int old_element = Feld[x][y];
10494 /* prevent changed element from moving in same engine frame
10495 unless both old and new element can either fall or move */
10496 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10497 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10502 CreateFieldExt(x, y, element, TRUE);
10505 static boolean ChangeElement(int x, int y, int element, int page)
10507 struct ElementInfo *ei = &element_info[element];
10508 struct ElementChangeInfo *change = &ei->change_page[page];
10509 int ce_value = CustomValue[x][y];
10510 int ce_score = ei->collect_score;
10511 int target_element;
10512 int old_element = Feld[x][y];
10514 /* always use default change event to prevent running into a loop */
10515 if (ChangeEvent[x][y] == -1)
10516 ChangeEvent[x][y] = CE_DELAY;
10518 if (ChangeEvent[x][y] == CE_DELAY)
10520 /* reset actual trigger element, trigger player and action element */
10521 change->actual_trigger_element = EL_EMPTY;
10522 change->actual_trigger_player = EL_PLAYER_1;
10523 change->actual_trigger_player_bits = CH_PLAYER_1;
10524 change->actual_trigger_side = CH_SIDE_NONE;
10525 change->actual_trigger_ce_value = 0;
10526 change->actual_trigger_ce_score = 0;
10529 /* do not change elements more than a specified maximum number of changes */
10530 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10533 ChangeCount[x][y]++; /* count number of changes in the same frame */
10535 if (change->explode)
10542 if (change->use_target_content)
10544 boolean complete_replace = TRUE;
10545 boolean can_replace[3][3];
10548 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10551 boolean is_walkable;
10552 boolean is_diggable;
10553 boolean is_collectible;
10554 boolean is_removable;
10555 boolean is_destructible;
10556 int ex = x + xx - 1;
10557 int ey = y + yy - 1;
10558 int content_element = change->target_content.e[xx][yy];
10561 can_replace[xx][yy] = TRUE;
10563 if (ex == x && ey == y) /* do not check changing element itself */
10566 if (content_element == EL_EMPTY_SPACE)
10568 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10573 if (!IN_LEV_FIELD(ex, ey))
10575 can_replace[xx][yy] = FALSE;
10576 complete_replace = FALSE;
10583 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10584 e = MovingOrBlocked2Element(ex, ey);
10586 is_empty = (IS_FREE(ex, ey) ||
10587 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10589 is_walkable = (is_empty || IS_WALKABLE(e));
10590 is_diggable = (is_empty || IS_DIGGABLE(e));
10591 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10592 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10593 is_removable = (is_diggable || is_collectible);
10595 can_replace[xx][yy] =
10596 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10597 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10598 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10599 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10600 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10601 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10602 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10604 if (!can_replace[xx][yy])
10605 complete_replace = FALSE;
10608 if (!change->only_if_complete || complete_replace)
10610 boolean something_has_changed = FALSE;
10612 if (change->only_if_complete && change->use_random_replace &&
10613 RND(100) < change->random_percentage)
10616 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10618 int ex = x + xx - 1;
10619 int ey = y + yy - 1;
10620 int content_element;
10622 if (can_replace[xx][yy] && (!change->use_random_replace ||
10623 RND(100) < change->random_percentage))
10625 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10626 RemoveMovingField(ex, ey);
10628 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10630 content_element = change->target_content.e[xx][yy];
10631 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10632 ce_value, ce_score);
10634 CreateElementFromChange(ex, ey, target_element);
10636 something_has_changed = TRUE;
10638 /* for symmetry reasons, freeze newly created border elements */
10639 if (ex != x || ey != y)
10640 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10644 if (something_has_changed)
10646 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10647 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10653 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10654 ce_value, ce_score);
10656 if (element == EL_DIAGONAL_GROWING ||
10657 element == EL_DIAGONAL_SHRINKING)
10659 target_element = Store[x][y];
10661 Store[x][y] = EL_EMPTY;
10664 CreateElementFromChange(x, y, target_element);
10666 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10667 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10670 /* this uses direct change before indirect change */
10671 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10676 #if USE_NEW_DELAYED_ACTION
10678 static void HandleElementChange(int x, int y, int page)
10680 int element = MovingOrBlocked2Element(x, y);
10681 struct ElementInfo *ei = &element_info[element];
10682 struct ElementChangeInfo *change = &ei->change_page[page];
10685 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10686 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10689 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10690 x, y, element, element_info[element].token_name);
10691 printf("HandleElementChange(): This should never happen!\n");
10696 /* this can happen with classic bombs on walkable, changing elements */
10697 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10700 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10701 ChangeDelay[x][y] = 0;
10707 if (ChangeDelay[x][y] == 0) /* initialize element change */
10709 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10711 if (change->can_change)
10714 /* !!! not clear why graphic animation should be reset at all here !!! */
10715 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10716 #if USE_GFX_RESET_WHEN_NOT_MOVING
10717 /* when a custom element is about to change (for example by change delay),
10718 do not reset graphic animation when the custom element is moving */
10719 if (!IS_MOVING(x, y))
10722 ResetGfxAnimation(x, y);
10723 ResetRandomAnimationValue(x, y);
10727 if (change->pre_change_function)
10728 change->pre_change_function(x, y);
10732 ChangeDelay[x][y]--;
10734 if (ChangeDelay[x][y] != 0) /* continue element change */
10736 if (change->can_change)
10738 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10740 if (IS_ANIMATED(graphic))
10741 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10743 if (change->change_function)
10744 change->change_function(x, y);
10747 else /* finish element change */
10749 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10751 page = ChangePage[x][y];
10752 ChangePage[x][y] = -1;
10754 change = &ei->change_page[page];
10757 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10759 ChangeDelay[x][y] = 1; /* try change after next move step */
10760 ChangePage[x][y] = page; /* remember page to use for change */
10765 if (change->can_change)
10767 if (ChangeElement(x, y, element, page))
10769 if (change->post_change_function)
10770 change->post_change_function(x, y);
10774 if (change->has_action)
10775 ExecuteCustomElementAction(x, y, element, page);
10781 static void HandleElementChange(int x, int y, int page)
10783 int element = MovingOrBlocked2Element(x, y);
10784 struct ElementInfo *ei = &element_info[element];
10785 struct ElementChangeInfo *change = &ei->change_page[page];
10788 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10791 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10792 x, y, element, element_info[element].token_name);
10793 printf("HandleElementChange(): This should never happen!\n");
10798 /* this can happen with classic bombs on walkable, changing elements */
10799 if (!CAN_CHANGE(element))
10802 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10803 ChangeDelay[x][y] = 0;
10809 if (ChangeDelay[x][y] == 0) /* initialize element change */
10811 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10813 ResetGfxAnimation(x, y);
10814 ResetRandomAnimationValue(x, y);
10816 if (change->pre_change_function)
10817 change->pre_change_function(x, y);
10820 ChangeDelay[x][y]--;
10822 if (ChangeDelay[x][y] != 0) /* continue element change */
10824 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10826 if (IS_ANIMATED(graphic))
10827 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10829 if (change->change_function)
10830 change->change_function(x, y);
10832 else /* finish element change */
10834 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10836 page = ChangePage[x][y];
10837 ChangePage[x][y] = -1;
10839 change = &ei->change_page[page];
10842 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10844 ChangeDelay[x][y] = 1; /* try change after next move step */
10845 ChangePage[x][y] = page; /* remember page to use for change */
10850 if (ChangeElement(x, y, element, page))
10852 if (change->post_change_function)
10853 change->post_change_function(x, y);
10860 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10861 int trigger_element,
10863 int trigger_player,
10867 boolean change_done_any = FALSE;
10868 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10871 if (!(trigger_events[trigger_element][trigger_event]))
10875 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10876 trigger_event, recursion_loop_depth, recursion_loop_detected,
10877 recursion_loop_element, EL_NAME(recursion_loop_element));
10880 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10882 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10884 int element = EL_CUSTOM_START + i;
10885 boolean change_done = FALSE;
10888 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10889 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10892 for (p = 0; p < element_info[element].num_change_pages; p++)
10894 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10896 if (change->can_change_or_has_action &&
10897 change->has_event[trigger_event] &&
10898 change->trigger_side & trigger_side &&
10899 change->trigger_player & trigger_player &&
10900 change->trigger_page & trigger_page_bits &&
10901 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10903 change->actual_trigger_element = trigger_element;
10904 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10905 change->actual_trigger_player_bits = trigger_player;
10906 change->actual_trigger_side = trigger_side;
10907 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10908 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10910 if ((change->can_change && !change_done) || change->has_action)
10914 SCAN_PLAYFIELD(x, y)
10916 if (Feld[x][y] == element)
10918 if (change->can_change && !change_done)
10920 ChangeDelay[x][y] = 1;
10921 ChangeEvent[x][y] = trigger_event;
10923 HandleElementChange(x, y, p);
10925 #if USE_NEW_DELAYED_ACTION
10926 else if (change->has_action)
10928 ExecuteCustomElementAction(x, y, element, p);
10929 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10932 if (change->has_action)
10934 ExecuteCustomElementAction(x, y, element, p);
10935 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10941 if (change->can_change)
10943 change_done = TRUE;
10944 change_done_any = TRUE;
10951 RECURSION_LOOP_DETECTION_END();
10953 return change_done_any;
10956 static boolean CheckElementChangeExt(int x, int y,
10958 int trigger_element,
10960 int trigger_player,
10963 boolean change_done = FALSE;
10966 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10967 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10970 if (Feld[x][y] == EL_BLOCKED)
10972 Blocked2Moving(x, y, &x, &y);
10973 element = Feld[x][y];
10977 /* check if element has already changed */
10978 if (Feld[x][y] != element)
10981 /* check if element has already changed or is about to change after moving */
10982 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10983 Feld[x][y] != element) ||
10985 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10986 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10987 ChangePage[x][y] != -1)))
10992 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10993 trigger_event, recursion_loop_depth, recursion_loop_detected,
10994 recursion_loop_element, EL_NAME(recursion_loop_element));
10997 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10999 for (p = 0; p < element_info[element].num_change_pages; p++)
11001 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11003 /* check trigger element for all events where the element that is checked
11004 for changing interacts with a directly adjacent element -- this is
11005 different to element changes that affect other elements to change on the
11006 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11007 boolean check_trigger_element =
11008 (trigger_event == CE_TOUCHING_X ||
11009 trigger_event == CE_HITTING_X ||
11010 trigger_event == CE_HIT_BY_X ||
11012 /* this one was forgotten until 3.2.3 */
11013 trigger_event == CE_DIGGING_X);
11016 if (change->can_change_or_has_action &&
11017 change->has_event[trigger_event] &&
11018 change->trigger_side & trigger_side &&
11019 change->trigger_player & trigger_player &&
11020 (!check_trigger_element ||
11021 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11023 change->actual_trigger_element = trigger_element;
11024 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11025 change->actual_trigger_player_bits = trigger_player;
11026 change->actual_trigger_side = trigger_side;
11027 change->actual_trigger_ce_value = CustomValue[x][y];
11028 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11030 /* special case: trigger element not at (x,y) position for some events */
11031 if (check_trigger_element)
11043 { 0, 0 }, { 0, 0 }, { 0, 0 },
11047 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11048 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11050 change->actual_trigger_ce_value = CustomValue[xx][yy];
11051 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11054 if (change->can_change && !change_done)
11056 ChangeDelay[x][y] = 1;
11057 ChangeEvent[x][y] = trigger_event;
11059 HandleElementChange(x, y, p);
11061 change_done = TRUE;
11063 #if USE_NEW_DELAYED_ACTION
11064 else if (change->has_action)
11066 ExecuteCustomElementAction(x, y, element, p);
11067 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11070 if (change->has_action)
11072 ExecuteCustomElementAction(x, y, element, p);
11073 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11079 RECURSION_LOOP_DETECTION_END();
11081 return change_done;
11084 static void PlayPlayerSound(struct PlayerInfo *player)
11086 int jx = player->jx, jy = player->jy;
11087 int sound_element = player->artwork_element;
11088 int last_action = player->last_action_waiting;
11089 int action = player->action_waiting;
11091 if (player->is_waiting)
11093 if (action != last_action)
11094 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11096 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11100 if (action != last_action)
11101 StopSound(element_info[sound_element].sound[last_action]);
11103 if (last_action == ACTION_SLEEPING)
11104 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11108 static void PlayAllPlayersSound()
11112 for (i = 0; i < MAX_PLAYERS; i++)
11113 if (stored_player[i].active)
11114 PlayPlayerSound(&stored_player[i]);
11117 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11119 boolean last_waiting = player->is_waiting;
11120 int move_dir = player->MovDir;
11122 player->dir_waiting = move_dir;
11123 player->last_action_waiting = player->action_waiting;
11127 if (!last_waiting) /* not waiting -> waiting */
11129 player->is_waiting = TRUE;
11131 player->frame_counter_bored =
11133 game.player_boring_delay_fixed +
11134 GetSimpleRandom(game.player_boring_delay_random);
11135 player->frame_counter_sleeping =
11137 game.player_sleeping_delay_fixed +
11138 GetSimpleRandom(game.player_sleeping_delay_random);
11140 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11143 if (game.player_sleeping_delay_fixed +
11144 game.player_sleeping_delay_random > 0 &&
11145 player->anim_delay_counter == 0 &&
11146 player->post_delay_counter == 0 &&
11147 FrameCounter >= player->frame_counter_sleeping)
11148 player->is_sleeping = TRUE;
11149 else if (game.player_boring_delay_fixed +
11150 game.player_boring_delay_random > 0 &&
11151 FrameCounter >= player->frame_counter_bored)
11152 player->is_bored = TRUE;
11154 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11155 player->is_bored ? ACTION_BORING :
11158 if (player->is_sleeping && player->use_murphy)
11160 /* special case for sleeping Murphy when leaning against non-free tile */
11162 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11163 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11164 !IS_MOVING(player->jx - 1, player->jy)))
11165 move_dir = MV_LEFT;
11166 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11167 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11168 !IS_MOVING(player->jx + 1, player->jy)))
11169 move_dir = MV_RIGHT;
11171 player->is_sleeping = FALSE;
11173 player->dir_waiting = move_dir;
11176 if (player->is_sleeping)
11178 if (player->num_special_action_sleeping > 0)
11180 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11182 int last_special_action = player->special_action_sleeping;
11183 int num_special_action = player->num_special_action_sleeping;
11184 int special_action =
11185 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11186 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11187 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11188 last_special_action + 1 : ACTION_SLEEPING);
11189 int special_graphic =
11190 el_act_dir2img(player->artwork_element, special_action, move_dir);
11192 player->anim_delay_counter =
11193 graphic_info[special_graphic].anim_delay_fixed +
11194 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11195 player->post_delay_counter =
11196 graphic_info[special_graphic].post_delay_fixed +
11197 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11199 player->special_action_sleeping = special_action;
11202 if (player->anim_delay_counter > 0)
11204 player->action_waiting = player->special_action_sleeping;
11205 player->anim_delay_counter--;
11207 else if (player->post_delay_counter > 0)
11209 player->post_delay_counter--;
11213 else if (player->is_bored)
11215 if (player->num_special_action_bored > 0)
11217 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11219 int special_action =
11220 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11221 int special_graphic =
11222 el_act_dir2img(player->artwork_element, special_action, move_dir);
11224 player->anim_delay_counter =
11225 graphic_info[special_graphic].anim_delay_fixed +
11226 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11227 player->post_delay_counter =
11228 graphic_info[special_graphic].post_delay_fixed +
11229 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11231 player->special_action_bored = special_action;
11234 if (player->anim_delay_counter > 0)
11236 player->action_waiting = player->special_action_bored;
11237 player->anim_delay_counter--;
11239 else if (player->post_delay_counter > 0)
11241 player->post_delay_counter--;
11246 else if (last_waiting) /* waiting -> not waiting */
11248 player->is_waiting = FALSE;
11249 player->is_bored = FALSE;
11250 player->is_sleeping = FALSE;
11252 player->frame_counter_bored = -1;
11253 player->frame_counter_sleeping = -1;
11255 player->anim_delay_counter = 0;
11256 player->post_delay_counter = 0;
11258 player->dir_waiting = player->MovDir;
11259 player->action_waiting = ACTION_DEFAULT;
11261 player->special_action_bored = ACTION_DEFAULT;
11262 player->special_action_sleeping = ACTION_DEFAULT;
11266 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11268 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11269 int left = player_action & JOY_LEFT;
11270 int right = player_action & JOY_RIGHT;
11271 int up = player_action & JOY_UP;
11272 int down = player_action & JOY_DOWN;
11273 int button1 = player_action & JOY_BUTTON_1;
11274 int button2 = player_action & JOY_BUTTON_2;
11275 int dx = (left ? -1 : right ? 1 : 0);
11276 int dy = (up ? -1 : down ? 1 : 0);
11278 if (!player->active || tape.pausing)
11284 snapped = SnapField(player, dx, dy);
11288 dropped = DropElement(player);
11290 moved = MovePlayer(player, dx, dy);
11293 if (tape.single_step && tape.recording && !tape.pausing)
11295 if (button1 || (dropped && !moved))
11297 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11298 SnapField(player, 0, 0); /* stop snapping */
11302 SetPlayerWaiting(player, FALSE);
11304 return player_action;
11308 /* no actions for this player (no input at player's configured device) */
11310 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11311 SnapField(player, 0, 0);
11312 CheckGravityMovementWhenNotMoving(player);
11314 if (player->MovPos == 0)
11315 SetPlayerWaiting(player, TRUE);
11317 if (player->MovPos == 0) /* needed for tape.playing */
11318 player->is_moving = FALSE;
11320 player->is_dropping = FALSE;
11321 player->is_dropping_pressed = FALSE;
11322 player->drop_pressed_delay = 0;
11328 static void CheckLevelTime()
11332 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11334 if (level.native_em_level->lev->home == 0) /* all players at home */
11336 PlayerWins(local_player);
11338 AllPlayersGone = TRUE;
11340 level.native_em_level->lev->home = -1;
11343 if (level.native_em_level->ply[0]->alive == 0 &&
11344 level.native_em_level->ply[1]->alive == 0 &&
11345 level.native_em_level->ply[2]->alive == 0 &&
11346 level.native_em_level->ply[3]->alive == 0) /* all dead */
11347 AllPlayersGone = TRUE;
11350 if (TimeFrames >= FRAMES_PER_SECOND)
11355 for (i = 0; i < MAX_PLAYERS; i++)
11357 struct PlayerInfo *player = &stored_player[i];
11359 if (SHIELD_ON(player))
11361 player->shield_normal_time_left--;
11363 if (player->shield_deadly_time_left > 0)
11364 player->shield_deadly_time_left--;
11368 if (!local_player->LevelSolved && !level.use_step_counter)
11376 if (TimeLeft <= 10 && setup.time_limit)
11377 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11380 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11382 DisplayGameControlValues();
11384 DrawGameValue_Time(TimeLeft);
11387 if (!TimeLeft && setup.time_limit)
11389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11390 level.native_em_level->lev->killed_out_of_time = TRUE;
11392 for (i = 0; i < MAX_PLAYERS; i++)
11393 KillPlayer(&stored_player[i]);
11397 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11399 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11401 DisplayGameControlValues();
11404 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11405 DrawGameValue_Time(TimePlayed);
11408 level.native_em_level->lev->time =
11409 (level.time == 0 ? TimePlayed : TimeLeft);
11412 if (tape.recording || tape.playing)
11413 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11416 UpdateGameDoorValues();
11417 DrawGameDoorValues();
11420 void AdvanceFrameAndPlayerCounters(int player_nr)
11424 /* advance frame counters (global frame counter and time frame counter) */
11428 /* advance player counters (counters for move delay, move animation etc.) */
11429 for (i = 0; i < MAX_PLAYERS; i++)
11431 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11432 int move_delay_value = stored_player[i].move_delay_value;
11433 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11435 if (!advance_player_counters) /* not all players may be affected */
11438 #if USE_NEW_PLAYER_ANIM
11439 if (move_frames == 0) /* less than one move per game frame */
11441 int stepsize = TILEX / move_delay_value;
11442 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11443 int count = (stored_player[i].is_moving ?
11444 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11446 if (count % delay == 0)
11451 stored_player[i].Frame += move_frames;
11453 if (stored_player[i].MovPos != 0)
11454 stored_player[i].StepFrame += move_frames;
11456 if (stored_player[i].move_delay > 0)
11457 stored_player[i].move_delay--;
11459 /* due to bugs in previous versions, counter must count up, not down */
11460 if (stored_player[i].push_delay != -1)
11461 stored_player[i].push_delay++;
11463 if (stored_player[i].drop_delay > 0)
11464 stored_player[i].drop_delay--;
11466 if (stored_player[i].is_dropping_pressed)
11467 stored_player[i].drop_pressed_delay++;
11471 void StartGameActions(boolean init_network_game, boolean record_tape,
11474 unsigned long new_random_seed = InitRND(random_seed);
11477 TapeStartRecording(new_random_seed);
11479 #if defined(NETWORK_AVALIABLE)
11480 if (init_network_game)
11482 SendToServer_StartPlaying();
11493 static unsigned long game_frame_delay = 0;
11494 unsigned long game_frame_delay_value;
11495 byte *recorded_player_action;
11496 byte summarized_player_action = 0;
11497 byte tape_action[MAX_PLAYERS];
11500 /* detect endless loops, caused by custom element programming */
11501 if (recursion_loop_detected && recursion_loop_depth == 0)
11503 char *message = getStringCat3("Internal Error ! Element ",
11504 EL_NAME(recursion_loop_element),
11505 " caused endless loop ! Quit the game ?");
11507 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11508 EL_NAME(recursion_loop_element));
11510 RequestQuitGameExt(FALSE, level_editor_test_game, message);
11512 recursion_loop_detected = FALSE; /* if game should be continued */
11519 if (game.restart_level)
11520 StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11522 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11524 if (level.native_em_level->lev->home == 0) /* all players at home */
11526 PlayerWins(local_player);
11528 AllPlayersGone = TRUE;
11530 level.native_em_level->lev->home = -1;
11533 if (level.native_em_level->ply[0]->alive == 0 &&
11534 level.native_em_level->ply[1]->alive == 0 &&
11535 level.native_em_level->ply[2]->alive == 0 &&
11536 level.native_em_level->ply[3]->alive == 0) /* all dead */
11537 AllPlayersGone = TRUE;
11540 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11543 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11546 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
11549 game_frame_delay_value =
11550 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11552 if (tape.playing && tape.warp_forward && !tape.pausing)
11553 game_frame_delay_value = 0;
11555 /* ---------- main game synchronization point ---------- */
11557 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11559 if (network_playing && !network_player_action_received)
11561 /* try to get network player actions in time */
11563 #if defined(NETWORK_AVALIABLE)
11564 /* last chance to get network player actions without main loop delay */
11565 HandleNetworking();
11568 /* game was quit by network peer */
11569 if (game_status != GAME_MODE_PLAYING)
11572 if (!network_player_action_received)
11573 return; /* failed to get network player actions in time */
11575 /* do not yet reset "network_player_action_received" (for tape.pausing) */
11581 /* at this point we know that we really continue executing the game */
11583 network_player_action_received = FALSE;
11585 /* when playing tape, read previously recorded player input from tape data */
11586 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11589 /* TapePlayAction() may return NULL when toggling to "pause before death" */
11594 if (tape.set_centered_player)
11596 game.centered_player_nr_next = tape.centered_player_nr_next;
11597 game.set_centered_player = TRUE;
11600 for (i = 0; i < MAX_PLAYERS; i++)
11602 summarized_player_action |= stored_player[i].action;
11604 if (!network_playing)
11605 stored_player[i].effective_action = stored_player[i].action;
11608 #if defined(NETWORK_AVALIABLE)
11609 if (network_playing)
11610 SendToServer_MovePlayer(summarized_player_action);
11613 if (!options.network && !setup.team_mode)
11614 local_player->effective_action = summarized_player_action;
11616 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11618 for (i = 0; i < MAX_PLAYERS; i++)
11619 stored_player[i].effective_action =
11620 (i == game.centered_player_nr ? summarized_player_action : 0);
11623 if (recorded_player_action != NULL)
11624 for (i = 0; i < MAX_PLAYERS; i++)
11625 stored_player[i].effective_action = recorded_player_action[i];
11627 for (i = 0; i < MAX_PLAYERS; i++)
11629 tape_action[i] = stored_player[i].effective_action;
11631 /* (this can only happen in the R'n'D game engine) */
11632 if (tape.recording && tape_action[i] && !tape.player_participates[i])
11633 tape.player_participates[i] = TRUE; /* player just appeared from CE */
11636 /* only record actions from input devices, but not programmed actions */
11637 if (tape.recording)
11638 TapeRecordAction(tape_action);
11640 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11642 GameActions_EM_Main();
11650 void GameActions_EM_Main()
11652 byte effective_action[MAX_PLAYERS];
11653 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11656 for (i = 0; i < MAX_PLAYERS; i++)
11657 effective_action[i] = stored_player[i].effective_action;
11659 GameActions_EM(effective_action, warp_mode);
11663 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
11666 void GameActions_RND()
11668 int magic_wall_x = 0, magic_wall_y = 0;
11669 int i, x, y, element, graphic;
11671 InitPlayfieldScanModeVars();
11673 #if USE_ONE_MORE_CHANGE_PER_FRAME
11674 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11676 SCAN_PLAYFIELD(x, y)
11678 ChangeCount[x][y] = 0;
11679 ChangeEvent[x][y] = -1;
11684 if (game.set_centered_player)
11686 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11688 /* switching to "all players" only possible if all players fit to screen */
11689 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11691 game.centered_player_nr_next = game.centered_player_nr;
11692 game.set_centered_player = FALSE;
11695 /* do not switch focus to non-existing (or non-active) player */
11696 if (game.centered_player_nr_next >= 0 &&
11697 !stored_player[game.centered_player_nr_next].active)
11699 game.centered_player_nr_next = game.centered_player_nr;
11700 game.set_centered_player = FALSE;
11704 if (game.set_centered_player &&
11705 ScreenMovPos == 0) /* screen currently aligned at tile position */
11709 if (game.centered_player_nr_next == -1)
11711 setScreenCenteredToAllPlayers(&sx, &sy);
11715 sx = stored_player[game.centered_player_nr_next].jx;
11716 sy = stored_player[game.centered_player_nr_next].jy;
11719 game.centered_player_nr = game.centered_player_nr_next;
11720 game.set_centered_player = FALSE;
11722 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11723 DrawGameDoorValues();
11726 for (i = 0; i < MAX_PLAYERS; i++)
11728 int actual_player_action = stored_player[i].effective_action;
11731 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11732 - rnd_equinox_tetrachloride 048
11733 - rnd_equinox_tetrachloride_ii 096
11734 - rnd_emanuel_schmieg 002
11735 - doctor_sloan_ww 001, 020
11737 if (stored_player[i].MovPos == 0)
11738 CheckGravityMovement(&stored_player[i]);
11741 /* overwrite programmed action with tape action */
11742 if (stored_player[i].programmed_action)
11743 actual_player_action = stored_player[i].programmed_action;
11745 PlayerActions(&stored_player[i], actual_player_action);
11747 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11750 ScrollScreen(NULL, SCROLL_GO_ON);
11752 /* for backwards compatibility, the following code emulates a fixed bug that
11753 occured when pushing elements (causing elements that just made their last
11754 pushing step to already (if possible) make their first falling step in the
11755 same game frame, which is bad); this code is also needed to use the famous
11756 "spring push bug" which is used in older levels and might be wanted to be
11757 used also in newer levels, but in this case the buggy pushing code is only
11758 affecting the "spring" element and no other elements */
11760 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11762 for (i = 0; i < MAX_PLAYERS; i++)
11764 struct PlayerInfo *player = &stored_player[i];
11765 int x = player->jx;
11766 int y = player->jy;
11768 if (player->active && player->is_pushing && player->is_moving &&
11770 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11771 Feld[x][y] == EL_SPRING))
11773 ContinueMoving(x, y);
11775 /* continue moving after pushing (this is actually a bug) */
11776 if (!IS_MOVING(x, y))
11777 Stop[x][y] = FALSE;
11783 debug_print_timestamp(0, "start main loop profiling");
11786 SCAN_PLAYFIELD(x, y)
11788 ChangeCount[x][y] = 0;
11789 ChangeEvent[x][y] = -1;
11791 /* this must be handled before main playfield loop */
11792 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11795 if (MovDelay[x][y] <= 0)
11799 #if USE_NEW_SNAP_DELAY
11800 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11803 if (MovDelay[x][y] <= 0)
11806 DrawLevelField(x, y);
11808 TestIfElementTouchesCustomElement(x, y); /* for empty space */
11814 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11816 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11817 printf("GameActions(): This should never happen!\n");
11819 ChangePage[x][y] = -1;
11823 Stop[x][y] = FALSE;
11824 if (WasJustMoving[x][y] > 0)
11825 WasJustMoving[x][y]--;
11826 if (WasJustFalling[x][y] > 0)
11827 WasJustFalling[x][y]--;
11828 if (CheckCollision[x][y] > 0)
11829 CheckCollision[x][y]--;
11830 if (CheckImpact[x][y] > 0)
11831 CheckImpact[x][y]--;
11835 /* reset finished pushing action (not done in ContinueMoving() to allow
11836 continuous pushing animation for elements with zero push delay) */
11837 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11839 ResetGfxAnimation(x, y);
11840 DrawLevelField(x, y);
11844 if (IS_BLOCKED(x, y))
11848 Blocked2Moving(x, y, &oldx, &oldy);
11849 if (!IS_MOVING(oldx, oldy))
11851 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11852 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11853 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11854 printf("GameActions(): This should never happen!\n");
11861 debug_print_timestamp(0, "- time for pre-main loop:");
11864 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
11865 SCAN_PLAYFIELD(x, y)
11867 element = Feld[x][y];
11868 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11873 int element2 = element;
11874 int graphic2 = graphic;
11876 int element2 = Feld[x][y];
11877 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11879 int last_gfx_frame = GfxFrame[x][y];
11881 if (graphic_info[graphic2].anim_global_sync)
11882 GfxFrame[x][y] = FrameCounter;
11883 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11884 GfxFrame[x][y] = CustomValue[x][y];
11885 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11886 GfxFrame[x][y] = element_info[element2].collect_score;
11887 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11888 GfxFrame[x][y] = ChangeDelay[x][y];
11890 if (redraw && GfxFrame[x][y] != last_gfx_frame)
11891 DrawLevelGraphicAnimation(x, y, graphic2);
11894 ResetGfxFrame(x, y, TRUE);
11898 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11899 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11900 ResetRandomAnimationValue(x, y);
11904 SetRandomAnimationValue(x, y);
11908 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11911 #endif // -------------------- !!! TEST ONLY !!! --------------------
11914 debug_print_timestamp(0, "- time for TEST loop: -->");
11917 SCAN_PLAYFIELD(x, y)
11919 element = Feld[x][y];
11920 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11922 ResetGfxFrame(x, y, TRUE);
11924 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11925 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11926 ResetRandomAnimationValue(x, y);
11928 SetRandomAnimationValue(x, y);
11930 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11932 if (IS_INACTIVE(element))
11934 if (IS_ANIMATED(graphic))
11935 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11940 /* this may take place after moving, so 'element' may have changed */
11941 if (IS_CHANGING(x, y) &&
11942 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11944 int page = element_info[element].event_page_nr[CE_DELAY];
11947 HandleElementChange(x, y, page);
11949 if (CAN_CHANGE(element))
11950 HandleElementChange(x, y, page);
11952 if (HAS_ACTION(element))
11953 ExecuteCustomElementAction(x, y, element, page);
11956 element = Feld[x][y];
11957 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11960 #if 0 // ---------------------------------------------------------------------
11962 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11966 element = Feld[x][y];
11967 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11969 if (IS_ANIMATED(graphic) &&
11970 !IS_MOVING(x, y) &&
11972 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11974 if (IS_GEM(element) || element == EL_SP_INFOTRON)
11975 DrawTwinkleOnField(x, y);
11977 else if (IS_MOVING(x, y))
11978 ContinueMoving(x, y);
11985 case EL_EM_EXIT_OPEN:
11986 case EL_SP_EXIT_OPEN:
11987 case EL_STEEL_EXIT_OPEN:
11988 case EL_EM_STEEL_EXIT_OPEN:
11989 case EL_SP_TERMINAL:
11990 case EL_SP_TERMINAL_ACTIVE:
11991 case EL_EXTRA_TIME:
11992 case EL_SHIELD_NORMAL:
11993 case EL_SHIELD_DEADLY:
11994 if (IS_ANIMATED(graphic))
11995 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11998 case EL_DYNAMITE_ACTIVE:
11999 case EL_EM_DYNAMITE_ACTIVE:
12000 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12001 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12002 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12003 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12004 case EL_SP_DISK_RED_ACTIVE:
12005 CheckDynamite(x, y);
12008 case EL_AMOEBA_GROWING:
12009 AmoebeWaechst(x, y);
12012 case EL_AMOEBA_SHRINKING:
12013 AmoebaDisappearing(x, y);
12016 #if !USE_NEW_AMOEBA_CODE
12017 case EL_AMOEBA_WET:
12018 case EL_AMOEBA_DRY:
12019 case EL_AMOEBA_FULL:
12021 case EL_EMC_DRIPPER:
12022 AmoebeAbleger(x, y);
12026 case EL_GAME_OF_LIFE:
12031 case EL_EXIT_CLOSED:
12035 case EL_EM_EXIT_CLOSED:
12039 case EL_STEEL_EXIT_CLOSED:
12040 CheckExitSteel(x, y);
12043 case EL_EM_STEEL_EXIT_CLOSED:
12044 CheckExitSteelEM(x, y);
12047 case EL_SP_EXIT_CLOSED:
12051 case EL_EXPANDABLE_WALL_GROWING:
12052 case EL_EXPANDABLE_STEELWALL_GROWING:
12053 MauerWaechst(x, y);
12056 case EL_EXPANDABLE_WALL:
12057 case EL_EXPANDABLE_WALL_HORIZONTAL:
12058 case EL_EXPANDABLE_WALL_VERTICAL:
12059 case EL_EXPANDABLE_WALL_ANY:
12060 case EL_BD_EXPANDABLE_WALL:
12061 MauerAbleger(x, y);
12064 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12065 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12066 case EL_EXPANDABLE_STEELWALL_ANY:
12067 MauerAblegerStahl(x, y);
12071 CheckForDragon(x, y);
12077 case EL_ELEMENT_SNAPPING:
12078 case EL_DIAGONAL_SHRINKING:
12079 case EL_DIAGONAL_GROWING:
12082 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12084 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12089 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12090 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12095 #else // ---------------------------------------------------------------------
12097 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12101 element = Feld[x][y];
12102 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12104 if (IS_ANIMATED(graphic) &&
12105 !IS_MOVING(x, y) &&
12107 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12109 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12110 DrawTwinkleOnField(x, y);
12112 else if ((element == EL_ACID ||
12113 element == EL_EXIT_OPEN ||
12114 element == EL_EM_EXIT_OPEN ||
12115 element == EL_SP_EXIT_OPEN ||
12116 element == EL_STEEL_EXIT_OPEN ||
12117 element == EL_EM_STEEL_EXIT_OPEN ||
12118 element == EL_SP_TERMINAL ||
12119 element == EL_SP_TERMINAL_ACTIVE ||
12120 element == EL_EXTRA_TIME ||
12121 element == EL_SHIELD_NORMAL ||
12122 element == EL_SHIELD_DEADLY) &&
12123 IS_ANIMATED(graphic))
12124 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12125 else if (IS_MOVING(x, y))
12126 ContinueMoving(x, y);
12127 else if (IS_ACTIVE_BOMB(element))
12128 CheckDynamite(x, y);
12129 else if (element == EL_AMOEBA_GROWING)
12130 AmoebeWaechst(x, y);
12131 else if (element == EL_AMOEBA_SHRINKING)
12132 AmoebaDisappearing(x, y);
12134 #if !USE_NEW_AMOEBA_CODE
12135 else if (IS_AMOEBALIVE(element))
12136 AmoebeAbleger(x, y);
12139 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12141 else if (element == EL_EXIT_CLOSED)
12143 else if (element == EL_EM_EXIT_CLOSED)
12145 else if (element == EL_STEEL_EXIT_CLOSED)
12146 CheckExitSteel(x, y);
12147 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12148 CheckExitSteelEM(x, y);
12149 else if (element == EL_SP_EXIT_CLOSED)
12151 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12152 element == EL_EXPANDABLE_STEELWALL_GROWING)
12153 MauerWaechst(x, y);
12154 else if (element == EL_EXPANDABLE_WALL ||
12155 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12156 element == EL_EXPANDABLE_WALL_VERTICAL ||
12157 element == EL_EXPANDABLE_WALL_ANY ||
12158 element == EL_BD_EXPANDABLE_WALL)
12159 MauerAbleger(x, y);
12160 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12161 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12162 element == EL_EXPANDABLE_STEELWALL_ANY)
12163 MauerAblegerStahl(x, y);
12164 else if (element == EL_FLAMES)
12165 CheckForDragon(x, y);
12166 else if (element == EL_EXPLOSION)
12167 ; /* drawing of correct explosion animation is handled separately */
12168 else if (element == EL_ELEMENT_SNAPPING ||
12169 element == EL_DIAGONAL_SHRINKING ||
12170 element == EL_DIAGONAL_GROWING)
12172 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12174 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12176 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12177 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12179 #endif // ---------------------------------------------------------------------
12181 if (IS_BELT_ACTIVE(element))
12182 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12184 if (game.magic_wall_active)
12186 int jx = local_player->jx, jy = local_player->jy;
12188 /* play the element sound at the position nearest to the player */
12189 if ((element == EL_MAGIC_WALL_FULL ||
12190 element == EL_MAGIC_WALL_ACTIVE ||
12191 element == EL_MAGIC_WALL_EMPTYING ||
12192 element == EL_BD_MAGIC_WALL_FULL ||
12193 element == EL_BD_MAGIC_WALL_ACTIVE ||
12194 element == EL_BD_MAGIC_WALL_EMPTYING ||
12195 element == EL_DC_MAGIC_WALL_FULL ||
12196 element == EL_DC_MAGIC_WALL_ACTIVE ||
12197 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12198 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12207 debug_print_timestamp(0, "- time for MAIN loop: -->");
12210 #if USE_NEW_AMOEBA_CODE
12211 /* new experimental amoeba growth stuff */
12212 if (!(FrameCounter % 8))
12214 static unsigned long random = 1684108901;
12216 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12218 x = RND(lev_fieldx);
12219 y = RND(lev_fieldy);
12220 element = Feld[x][y];
12222 if (!IS_PLAYER(x,y) &&
12223 (element == EL_EMPTY ||
12224 CAN_GROW_INTO(element) ||
12225 element == EL_QUICKSAND_EMPTY ||
12226 element == EL_QUICKSAND_FAST_EMPTY ||
12227 element == EL_ACID_SPLASH_LEFT ||
12228 element == EL_ACID_SPLASH_RIGHT))
12230 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12231 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12232 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12233 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12234 Feld[x][y] = EL_AMOEBA_DROP;
12237 random = random * 129 + 1;
12243 if (game.explosions_delayed)
12246 game.explosions_delayed = FALSE;
12248 SCAN_PLAYFIELD(x, y)
12250 element = Feld[x][y];
12252 if (ExplodeField[x][y])
12253 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12254 else if (element == EL_EXPLOSION)
12255 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12257 ExplodeField[x][y] = EX_TYPE_NONE;
12260 game.explosions_delayed = TRUE;
12263 if (game.magic_wall_active)
12265 if (!(game.magic_wall_time_left % 4))
12267 int element = Feld[magic_wall_x][magic_wall_y];
12269 if (element == EL_BD_MAGIC_WALL_FULL ||
12270 element == EL_BD_MAGIC_WALL_ACTIVE ||
12271 element == EL_BD_MAGIC_WALL_EMPTYING)
12272 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12273 else if (element == EL_DC_MAGIC_WALL_FULL ||
12274 element == EL_DC_MAGIC_WALL_ACTIVE ||
12275 element == EL_DC_MAGIC_WALL_EMPTYING)
12276 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12278 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12281 if (game.magic_wall_time_left > 0)
12283 game.magic_wall_time_left--;
12285 if (!game.magic_wall_time_left)
12287 SCAN_PLAYFIELD(x, y)
12289 element = Feld[x][y];
12291 if (element == EL_MAGIC_WALL_ACTIVE ||
12292 element == EL_MAGIC_WALL_FULL)
12294 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12295 DrawLevelField(x, y);
12297 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12298 element == EL_BD_MAGIC_WALL_FULL)
12300 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12301 DrawLevelField(x, y);
12303 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12304 element == EL_DC_MAGIC_WALL_FULL)
12306 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12307 DrawLevelField(x, y);
12311 game.magic_wall_active = FALSE;
12316 if (game.light_time_left > 0)
12318 game.light_time_left--;
12320 if (game.light_time_left == 0)
12321 RedrawAllLightSwitchesAndInvisibleElements();
12324 if (game.timegate_time_left > 0)
12326 game.timegate_time_left--;
12328 if (game.timegate_time_left == 0)
12329 CloseAllOpenTimegates();
12332 if (game.lenses_time_left > 0)
12334 game.lenses_time_left--;
12336 if (game.lenses_time_left == 0)
12337 RedrawAllInvisibleElementsForLenses();
12340 if (game.magnify_time_left > 0)
12342 game.magnify_time_left--;
12344 if (game.magnify_time_left == 0)
12345 RedrawAllInvisibleElementsForMagnifier();
12348 for (i = 0; i < MAX_PLAYERS; i++)
12350 struct PlayerInfo *player = &stored_player[i];
12352 if (SHIELD_ON(player))
12354 if (player->shield_deadly_time_left)
12355 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12356 else if (player->shield_normal_time_left)
12357 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12364 PlayAllPlayersSound();
12366 if (options.debug) /* calculate frames per second */
12368 static unsigned long fps_counter = 0;
12369 static int fps_frames = 0;
12370 unsigned long fps_delay_ms = Counter() - fps_counter;
12374 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
12376 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12379 fps_counter = Counter();
12382 redraw_mask |= REDRAW_FPS;
12385 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12387 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12389 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12391 local_player->show_envelope = 0;
12395 debug_print_timestamp(0, "stop main loop profiling ");
12396 printf("----------------------------------------------------------\n");
12399 /* use random number generator in every frame to make it less predictable */
12400 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12404 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12406 int min_x = x, min_y = y, max_x = x, max_y = y;
12409 for (i = 0; i < MAX_PLAYERS; i++)
12411 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12413 if (!stored_player[i].active || &stored_player[i] == player)
12416 min_x = MIN(min_x, jx);
12417 min_y = MIN(min_y, jy);
12418 max_x = MAX(max_x, jx);
12419 max_y = MAX(max_y, jy);
12422 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12425 static boolean AllPlayersInVisibleScreen()
12429 for (i = 0; i < MAX_PLAYERS; i++)
12431 int jx = stored_player[i].jx, jy = stored_player[i].jy;
12433 if (!stored_player[i].active)
12436 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12443 void ScrollLevel(int dx, int dy)
12446 static Bitmap *bitmap_db_field2 = NULL;
12447 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12454 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12455 /* only horizontal XOR vertical scroll direction allowed */
12456 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12461 if (bitmap_db_field2 == NULL)
12462 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12464 /* needed when blitting directly to same bitmap -- should not be needed with
12465 recent SDL libraries, but apparently does not work in 1.2.11 directly */
12466 BlitBitmap(drawto_field, bitmap_db_field2,
12467 FX + TILEX * (dx == -1) - softscroll_offset,
12468 FY + TILEY * (dy == -1) - softscroll_offset,
12469 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12470 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12471 FX + TILEX * (dx == 1) - softscroll_offset,
12472 FY + TILEY * (dy == 1) - softscroll_offset);
12473 BlitBitmap(bitmap_db_field2, drawto_field,
12474 FX + TILEX * (dx == 1) - softscroll_offset,
12475 FY + TILEY * (dy == 1) - softscroll_offset,
12476 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12477 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12478 FX + TILEX * (dx == 1) - softscroll_offset,
12479 FY + TILEY * (dy == 1) - softscroll_offset);
12484 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12485 int xsize = (BX2 - BX1 + 1);
12486 int ysize = (BY2 - BY1 + 1);
12487 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12488 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12489 int step = (start < end ? +1 : -1);
12491 for (i = start; i != end; i += step)
12493 BlitBitmap(drawto_field, drawto_field,
12494 FX + TILEX * (dx != 0 ? i + step : 0),
12495 FY + TILEY * (dy != 0 ? i + step : 0),
12496 TILEX * (dx != 0 ? 1 : xsize),
12497 TILEY * (dy != 0 ? 1 : ysize),
12498 FX + TILEX * (dx != 0 ? i : 0),
12499 FY + TILEY * (dy != 0 ? i : 0));
12504 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12506 BlitBitmap(drawto_field, drawto_field,
12507 FX + TILEX * (dx == -1) - softscroll_offset,
12508 FY + TILEY * (dy == -1) - softscroll_offset,
12509 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12510 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12511 FX + TILEX * (dx == 1) - softscroll_offset,
12512 FY + TILEY * (dy == 1) - softscroll_offset);
12518 x = (dx == 1 ? BX1 : BX2);
12519 for (y = BY1; y <= BY2; y++)
12520 DrawScreenField(x, y);
12525 y = (dy == 1 ? BY1 : BY2);
12526 for (x = BX1; x <= BX2; x++)
12527 DrawScreenField(x, y);
12530 redraw_mask |= REDRAW_FIELD;
12533 static boolean canFallDown(struct PlayerInfo *player)
12535 int jx = player->jx, jy = player->jy;
12537 return (IN_LEV_FIELD(jx, jy + 1) &&
12538 (IS_FREE(jx, jy + 1) ||
12539 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12540 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12541 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12544 static boolean canPassField(int x, int y, int move_dir)
12546 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12547 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12548 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12549 int nextx = x + dx;
12550 int nexty = y + dy;
12551 int element = Feld[x][y];
12553 return (IS_PASSABLE_FROM(element, opposite_dir) &&
12554 !CAN_MOVE(element) &&
12555 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12556 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12557 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12560 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12562 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12563 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12564 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
12568 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12569 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12570 (IS_DIGGABLE(Feld[newx][newy]) ||
12571 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12572 canPassField(newx, newy, move_dir)));
12575 static void CheckGravityMovement(struct PlayerInfo *player)
12577 #if USE_PLAYER_GRAVITY
12578 if (player->gravity && !player->programmed_action)
12580 if (game.gravity && !player->programmed_action)
12583 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12584 int move_dir_vertical = player->effective_action & MV_VERTICAL;
12585 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12586 int jx = player->jx, jy = player->jy;
12587 boolean player_is_moving_to_valid_field =
12588 (!player_is_snapping &&
12589 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12590 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12591 boolean player_can_fall_down = canFallDown(player);
12593 if (player_can_fall_down &&
12594 !player_is_moving_to_valid_field)
12595 player->programmed_action = MV_DOWN;
12599 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12601 return CheckGravityMovement(player);
12603 #if USE_PLAYER_GRAVITY
12604 if (player->gravity && !player->programmed_action)
12606 if (game.gravity && !player->programmed_action)
12609 int jx = player->jx, jy = player->jy;
12610 boolean field_under_player_is_free =
12611 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12612 boolean player_is_standing_on_valid_field =
12613 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12614 (IS_WALKABLE(Feld[jx][jy]) &&
12615 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12617 if (field_under_player_is_free && !player_is_standing_on_valid_field)
12618 player->programmed_action = MV_DOWN;
12623 MovePlayerOneStep()
12624 -----------------------------------------------------------------------------
12625 dx, dy: direction (non-diagonal) to try to move the player to
12626 real_dx, real_dy: direction as read from input device (can be diagonal)
12629 boolean MovePlayerOneStep(struct PlayerInfo *player,
12630 int dx, int dy, int real_dx, int real_dy)
12632 int jx = player->jx, jy = player->jy;
12633 int new_jx = jx + dx, new_jy = jy + dy;
12634 #if !USE_FIXED_DONT_RUN_INTO
12638 boolean player_can_move = !player->cannot_move;
12640 if (!player->active || (!dx && !dy))
12641 return MP_NO_ACTION;
12643 player->MovDir = (dx < 0 ? MV_LEFT :
12644 dx > 0 ? MV_RIGHT :
12646 dy > 0 ? MV_DOWN : MV_NONE);
12648 if (!IN_LEV_FIELD(new_jx, new_jy))
12649 return MP_NO_ACTION;
12651 if (!player_can_move)
12653 if (player->MovPos == 0)
12655 player->is_moving = FALSE;
12656 player->is_digging = FALSE;
12657 player->is_collecting = FALSE;
12658 player->is_snapping = FALSE;
12659 player->is_pushing = FALSE;
12664 if (!options.network && game.centered_player_nr == -1 &&
12665 !AllPlayersInSight(player, new_jx, new_jy))
12666 return MP_NO_ACTION;
12668 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12669 return MP_NO_ACTION;
12672 #if !USE_FIXED_DONT_RUN_INTO
12673 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12675 /* (moved to DigField()) */
12676 if (player_can_move && DONT_RUN_INTO(element))
12678 if (element == EL_ACID && dx == 0 && dy == 1)
12680 SplashAcid(new_jx, new_jy);
12681 Feld[jx][jy] = EL_PLAYER_1;
12682 InitMovingField(jx, jy, MV_DOWN);
12683 Store[jx][jy] = EL_ACID;
12684 ContinueMoving(jx, jy);
12685 BuryPlayer(player);
12688 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12694 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12695 if (can_move != MP_MOVING)
12698 /* check if DigField() has caused relocation of the player */
12699 if (player->jx != jx || player->jy != jy)
12700 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12702 StorePlayer[jx][jy] = 0;
12703 player->last_jx = jx;
12704 player->last_jy = jy;
12705 player->jx = new_jx;
12706 player->jy = new_jy;
12707 StorePlayer[new_jx][new_jy] = player->element_nr;
12709 if (player->move_delay_value_next != -1)
12711 player->move_delay_value = player->move_delay_value_next;
12712 player->move_delay_value_next = -1;
12716 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12718 player->step_counter++;
12720 PlayerVisit[jx][jy] = FrameCounter;
12722 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12723 player->is_moving = TRUE;
12727 /* should better be called in MovePlayer(), but this breaks some tapes */
12728 ScrollPlayer(player, SCROLL_INIT);
12734 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12736 int jx = player->jx, jy = player->jy;
12737 int old_jx = jx, old_jy = jy;
12738 int moved = MP_NO_ACTION;
12740 if (!player->active)
12745 if (player->MovPos == 0)
12747 player->is_moving = FALSE;
12748 player->is_digging = FALSE;
12749 player->is_collecting = FALSE;
12750 player->is_snapping = FALSE;
12751 player->is_pushing = FALSE;
12757 if (player->move_delay > 0)
12760 player->move_delay = -1; /* set to "uninitialized" value */
12762 /* store if player is automatically moved to next field */
12763 player->is_auto_moving = (player->programmed_action != MV_NONE);
12765 /* remove the last programmed player action */
12766 player->programmed_action = 0;
12768 if (player->MovPos)
12770 /* should only happen if pre-1.2 tape recordings are played */
12771 /* this is only for backward compatibility */
12773 int original_move_delay_value = player->move_delay_value;
12776 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12780 /* scroll remaining steps with finest movement resolution */
12781 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12783 while (player->MovPos)
12785 ScrollPlayer(player, SCROLL_GO_ON);
12786 ScrollScreen(NULL, SCROLL_GO_ON);
12788 AdvanceFrameAndPlayerCounters(player->index_nr);
12794 player->move_delay_value = original_move_delay_value;
12797 player->is_active = FALSE;
12799 if (player->last_move_dir & MV_HORIZONTAL)
12801 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12802 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12806 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12807 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12810 #if USE_FIXED_BORDER_RUNNING_GFX
12811 if (!moved && !player->is_active)
12813 player->is_moving = FALSE;
12814 player->is_digging = FALSE;
12815 player->is_collecting = FALSE;
12816 player->is_snapping = FALSE;
12817 player->is_pushing = FALSE;
12825 if (moved & MP_MOVING && !ScreenMovPos &&
12826 (player->index_nr == game.centered_player_nr ||
12827 game.centered_player_nr == -1))
12829 if (moved & MP_MOVING && !ScreenMovPos &&
12830 (player == local_player || !options.network))
12833 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12834 int offset = game.scroll_delay_value;
12836 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12838 /* actual player has left the screen -- scroll in that direction */
12839 if (jx != old_jx) /* player has moved horizontally */
12840 scroll_x += (jx - old_jx);
12841 else /* player has moved vertically */
12842 scroll_y += (jy - old_jy);
12846 if (jx != old_jx) /* player has moved horizontally */
12848 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
12849 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12850 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12852 /* don't scroll over playfield boundaries */
12853 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12854 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12856 /* don't scroll more than one field at a time */
12857 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12859 /* don't scroll against the player's moving direction */
12860 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
12861 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12862 scroll_x = old_scroll_x;
12864 else /* player has moved vertically */
12866 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
12867 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12868 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12870 /* don't scroll over playfield boundaries */
12871 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12872 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12874 /* don't scroll more than one field at a time */
12875 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12877 /* don't scroll against the player's moving direction */
12878 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
12879 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12880 scroll_y = old_scroll_y;
12884 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12887 if (!options.network && game.centered_player_nr == -1 &&
12888 !AllPlayersInVisibleScreen())
12890 scroll_x = old_scroll_x;
12891 scroll_y = old_scroll_y;
12895 if (!options.network && !AllPlayersInVisibleScreen())
12897 scroll_x = old_scroll_x;
12898 scroll_y = old_scroll_y;
12903 ScrollScreen(player, SCROLL_INIT);
12904 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12909 player->StepFrame = 0;
12911 if (moved & MP_MOVING)
12913 if (old_jx != jx && old_jy == jy)
12914 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12915 else if (old_jx == jx && old_jy != jy)
12916 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12918 DrawLevelField(jx, jy); /* for "crumbled sand" */
12920 player->last_move_dir = player->MovDir;
12921 player->is_moving = TRUE;
12922 player->is_snapping = FALSE;
12923 player->is_switching = FALSE;
12924 player->is_dropping = FALSE;
12925 player->is_dropping_pressed = FALSE;
12926 player->drop_pressed_delay = 0;
12929 /* should better be called here than above, but this breaks some tapes */
12930 ScrollPlayer(player, SCROLL_INIT);
12935 CheckGravityMovementWhenNotMoving(player);
12937 player->is_moving = FALSE;
12939 /* at this point, the player is allowed to move, but cannot move right now
12940 (e.g. because of something blocking the way) -- ensure that the player
12941 is also allowed to move in the next frame (in old versions before 3.1.1,
12942 the player was forced to wait again for eight frames before next try) */
12944 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12945 player->move_delay = 0; /* allow direct movement in the next frame */
12948 if (player->move_delay == -1) /* not yet initialized by DigField() */
12949 player->move_delay = player->move_delay_value;
12951 if (game.engine_version < VERSION_IDENT(3,0,7,0))
12953 TestIfPlayerTouchesBadThing(jx, jy);
12954 TestIfPlayerTouchesCustomElement(jx, jy);
12957 if (!player->active)
12958 RemovePlayer(player);
12963 void ScrollPlayer(struct PlayerInfo *player, int mode)
12965 int jx = player->jx, jy = player->jy;
12966 int last_jx = player->last_jx, last_jy = player->last_jy;
12967 int move_stepsize = TILEX / player->move_delay_value;
12969 #if USE_NEW_PLAYER_SPEED
12970 if (!player->active)
12973 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
12976 if (!player->active || player->MovPos == 0)
12980 if (mode == SCROLL_INIT)
12982 player->actual_frame_counter = FrameCounter;
12983 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12985 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12986 Feld[last_jx][last_jy] == EL_EMPTY)
12988 int last_field_block_delay = 0; /* start with no blocking at all */
12989 int block_delay_adjustment = player->block_delay_adjustment;
12991 /* if player blocks last field, add delay for exactly one move */
12992 if (player->block_last_field)
12994 last_field_block_delay += player->move_delay_value;
12996 /* when blocking enabled, prevent moving up despite gravity */
12997 #if USE_PLAYER_GRAVITY
12998 if (player->gravity && player->MovDir == MV_UP)
12999 block_delay_adjustment = -1;
13001 if (game.gravity && player->MovDir == MV_UP)
13002 block_delay_adjustment = -1;
13006 /* add block delay adjustment (also possible when not blocking) */
13007 last_field_block_delay += block_delay_adjustment;
13009 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13010 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13013 #if USE_NEW_PLAYER_SPEED
13014 if (player->MovPos != 0) /* player has not yet reached destination */
13020 else if (!FrameReached(&player->actual_frame_counter, 1))
13023 #if USE_NEW_PLAYER_SPEED
13024 if (player->MovPos != 0)
13026 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13027 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13029 /* before DrawPlayer() to draw correct player graphic for this case */
13030 if (player->MovPos == 0)
13031 CheckGravityMovement(player);
13034 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13035 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13037 /* before DrawPlayer() to draw correct player graphic for this case */
13038 if (player->MovPos == 0)
13039 CheckGravityMovement(player);
13042 if (player->MovPos == 0) /* player reached destination field */
13044 if (player->move_delay_reset_counter > 0)
13046 player->move_delay_reset_counter--;
13048 if (player->move_delay_reset_counter == 0)
13050 /* continue with normal speed after quickly moving through gate */
13051 HALVE_PLAYER_SPEED(player);
13053 /* be able to make the next move without delay */
13054 player->move_delay = 0;
13058 player->last_jx = jx;
13059 player->last_jy = jy;
13061 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13062 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13063 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13064 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13065 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13066 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13068 DrawPlayer(player); /* needed here only to cleanup last field */
13069 RemovePlayer(player);
13071 if (local_player->friends_still_needed == 0 ||
13072 IS_SP_ELEMENT(Feld[jx][jy]))
13073 PlayerWins(player);
13076 /* this breaks one level: "machine", level 000 */
13078 int move_direction = player->MovDir;
13079 int enter_side = MV_DIR_OPPOSITE(move_direction);
13080 int leave_side = move_direction;
13081 int old_jx = last_jx;
13082 int old_jy = last_jy;
13083 int old_element = Feld[old_jx][old_jy];
13084 int new_element = Feld[jx][jy];
13086 if (IS_CUSTOM_ELEMENT(old_element))
13087 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13089 player->index_bit, leave_side);
13091 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13092 CE_PLAYER_LEAVES_X,
13093 player->index_bit, leave_side);
13095 if (IS_CUSTOM_ELEMENT(new_element))
13096 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13097 player->index_bit, enter_side);
13099 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13100 CE_PLAYER_ENTERS_X,
13101 player->index_bit, enter_side);
13103 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13104 CE_MOVE_OF_X, move_direction);
13107 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13109 TestIfPlayerTouchesBadThing(jx, jy);
13110 TestIfPlayerTouchesCustomElement(jx, jy);
13112 /* needed because pushed element has not yet reached its destination,
13113 so it would trigger a change event at its previous field location */
13114 if (!player->is_pushing)
13115 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13117 if (!player->active)
13118 RemovePlayer(player);
13121 if (!local_player->LevelSolved && level.use_step_counter)
13131 if (TimeLeft <= 10 && setup.time_limit)
13132 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13135 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13137 DisplayGameControlValues();
13139 DrawGameValue_Time(TimeLeft);
13142 if (!TimeLeft && setup.time_limit)
13143 for (i = 0; i < MAX_PLAYERS; i++)
13144 KillPlayer(&stored_player[i]);
13147 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13149 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13151 DisplayGameControlValues();
13154 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13155 DrawGameValue_Time(TimePlayed);
13159 if (tape.single_step && tape.recording && !tape.pausing &&
13160 !player->programmed_action)
13161 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13165 void ScrollScreen(struct PlayerInfo *player, int mode)
13167 static unsigned long screen_frame_counter = 0;
13169 if (mode == SCROLL_INIT)
13171 /* set scrolling step size according to actual player's moving speed */
13172 ScrollStepSize = TILEX / player->move_delay_value;
13174 screen_frame_counter = FrameCounter;
13175 ScreenMovDir = player->MovDir;
13176 ScreenMovPos = player->MovPos;
13177 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13180 else if (!FrameReached(&screen_frame_counter, 1))
13185 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13186 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13187 redraw_mask |= REDRAW_FIELD;
13190 ScreenMovDir = MV_NONE;
13193 void TestIfPlayerTouchesCustomElement(int x, int y)
13195 static int xy[4][2] =
13202 static int trigger_sides[4][2] =
13204 /* center side border side */
13205 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13206 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13207 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13208 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13210 static int touch_dir[4] =
13212 MV_LEFT | MV_RIGHT,
13217 int center_element = Feld[x][y]; /* should always be non-moving! */
13220 for (i = 0; i < NUM_DIRECTIONS; i++)
13222 int xx = x + xy[i][0];
13223 int yy = y + xy[i][1];
13224 int center_side = trigger_sides[i][0];
13225 int border_side = trigger_sides[i][1];
13226 int border_element;
13228 if (!IN_LEV_FIELD(xx, yy))
13231 if (IS_PLAYER(x, y))
13233 struct PlayerInfo *player = PLAYERINFO(x, y);
13235 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13236 border_element = Feld[xx][yy]; /* may be moving! */
13237 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13238 border_element = Feld[xx][yy];
13239 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13240 border_element = MovingOrBlocked2Element(xx, yy);
13242 continue; /* center and border element do not touch */
13244 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13245 player->index_bit, border_side);
13246 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13247 CE_PLAYER_TOUCHES_X,
13248 player->index_bit, border_side);
13250 else if (IS_PLAYER(xx, yy))
13252 struct PlayerInfo *player = PLAYERINFO(xx, yy);
13254 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13256 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13257 continue; /* center and border element do not touch */
13260 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13261 player->index_bit, center_side);
13262 CheckTriggeredElementChangeByPlayer(x, y, center_element,
13263 CE_PLAYER_TOUCHES_X,
13264 player->index_bit, center_side);
13270 #if USE_ELEMENT_TOUCHING_BUGFIX
13272 void TestIfElementTouchesCustomElement(int x, int y)
13274 static int xy[4][2] =
13281 static int trigger_sides[4][2] =
13283 /* center side border side */
13284 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13285 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13286 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13287 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13289 static int touch_dir[4] =
13291 MV_LEFT | MV_RIGHT,
13296 boolean change_center_element = FALSE;
13297 int center_element = Feld[x][y]; /* should always be non-moving! */
13298 int border_element_old[NUM_DIRECTIONS];
13301 for (i = 0; i < NUM_DIRECTIONS; i++)
13303 int xx = x + xy[i][0];
13304 int yy = y + xy[i][1];
13305 int border_element;
13307 border_element_old[i] = -1;
13309 if (!IN_LEV_FIELD(xx, yy))
13312 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13313 border_element = Feld[xx][yy]; /* may be moving! */
13314 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13315 border_element = Feld[xx][yy];
13316 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13317 border_element = MovingOrBlocked2Element(xx, yy);
13319 continue; /* center and border element do not touch */
13321 border_element_old[i] = border_element;
13324 for (i = 0; i < NUM_DIRECTIONS; i++)
13326 int xx = x + xy[i][0];
13327 int yy = y + xy[i][1];
13328 int center_side = trigger_sides[i][0];
13329 int border_element = border_element_old[i];
13331 if (border_element == -1)
13334 /* check for change of border element */
13335 CheckElementChangeBySide(xx, yy, border_element, center_element,
13336 CE_TOUCHING_X, center_side);
13339 for (i = 0; i < NUM_DIRECTIONS; i++)
13341 int border_side = trigger_sides[i][1];
13342 int border_element = border_element_old[i];
13344 if (border_element == -1)
13347 /* check for change of center element (but change it only once) */
13348 if (!change_center_element)
13349 change_center_element =
13350 CheckElementChangeBySide(x, y, center_element, border_element,
13351 CE_TOUCHING_X, border_side);
13357 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13359 static int xy[4][2] =
13366 static int trigger_sides[4][2] =
13368 /* center side border side */
13369 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13370 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13371 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13372 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13374 static int touch_dir[4] =
13376 MV_LEFT | MV_RIGHT,
13381 boolean change_center_element = FALSE;
13382 int center_element = Feld[x][y]; /* should always be non-moving! */
13385 for (i = 0; i < NUM_DIRECTIONS; i++)
13387 int xx = x + xy[i][0];
13388 int yy = y + xy[i][1];
13389 int center_side = trigger_sides[i][0];
13390 int border_side = trigger_sides[i][1];
13391 int border_element;
13393 if (!IN_LEV_FIELD(xx, yy))
13396 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13397 border_element = Feld[xx][yy]; /* may be moving! */
13398 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13399 border_element = Feld[xx][yy];
13400 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13401 border_element = MovingOrBlocked2Element(xx, yy);
13403 continue; /* center and border element do not touch */
13405 /* check for change of center element (but change it only once) */
13406 if (!change_center_element)
13407 change_center_element =
13408 CheckElementChangeBySide(x, y, center_element, border_element,
13409 CE_TOUCHING_X, border_side);
13411 /* check for change of border element */
13412 CheckElementChangeBySide(xx, yy, border_element, center_element,
13413 CE_TOUCHING_X, center_side);
13419 void TestIfElementHitsCustomElement(int x, int y, int direction)
13421 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13422 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13423 int hitx = x + dx, hity = y + dy;
13424 int hitting_element = Feld[x][y];
13425 int touched_element;
13427 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13430 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13431 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13433 if (IN_LEV_FIELD(hitx, hity))
13435 int opposite_direction = MV_DIR_OPPOSITE(direction);
13436 int hitting_side = direction;
13437 int touched_side = opposite_direction;
13438 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13439 MovDir[hitx][hity] != direction ||
13440 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13446 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13447 CE_HITTING_X, touched_side);
13449 CheckElementChangeBySide(hitx, hity, touched_element,
13450 hitting_element, CE_HIT_BY_X, hitting_side);
13452 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13453 CE_HIT_BY_SOMETHING, opposite_direction);
13457 /* "hitting something" is also true when hitting the playfield border */
13458 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13459 CE_HITTING_SOMETHING, direction);
13463 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13465 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13466 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
13467 int hitx = x + dx, hity = y + dy;
13468 int hitting_element = Feld[x][y];
13469 int touched_element;
13471 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13472 !IS_FREE(hitx, hity) &&
13473 (!IS_MOVING(hitx, hity) ||
13474 MovDir[hitx][hity] != direction ||
13475 ABS(MovPos[hitx][hity]) <= TILEY / 2));
13478 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13482 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13486 touched_element = (IN_LEV_FIELD(hitx, hity) ?
13487 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13489 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13490 EP_CAN_SMASH_EVERYTHING, direction);
13492 if (IN_LEV_FIELD(hitx, hity))
13494 int opposite_direction = MV_DIR_OPPOSITE(direction);
13495 int hitting_side = direction;
13496 int touched_side = opposite_direction;
13498 int touched_element = MovingOrBlocked2Element(hitx, hity);
13501 boolean object_hit = (!IS_MOVING(hitx, hity) ||
13502 MovDir[hitx][hity] != direction ||
13503 ABS(MovPos[hitx][hity]) <= TILEY / 2);
13512 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13513 CE_SMASHED_BY_SOMETHING, opposite_direction);
13515 CheckElementChangeBySide(x, y, hitting_element, touched_element,
13516 CE_OTHER_IS_SMASHING, touched_side);
13518 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13519 CE_OTHER_GETS_SMASHED, hitting_side);
13525 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13527 int i, kill_x = -1, kill_y = -1;
13529 int bad_element = -1;
13530 static int test_xy[4][2] =
13537 static int test_dir[4] =
13545 for (i = 0; i < NUM_DIRECTIONS; i++)
13547 int test_x, test_y, test_move_dir, test_element;
13549 test_x = good_x + test_xy[i][0];
13550 test_y = good_y + test_xy[i][1];
13552 if (!IN_LEV_FIELD(test_x, test_y))
13556 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13558 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13560 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13561 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13563 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13564 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
13568 bad_element = test_element;
13574 if (kill_x != -1 || kill_y != -1)
13576 if (IS_PLAYER(good_x, good_y))
13578 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13580 if (player->shield_deadly_time_left > 0 &&
13581 !IS_INDESTRUCTIBLE(bad_element))
13582 Bang(kill_x, kill_y);
13583 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13584 KillPlayer(player);
13587 Bang(good_x, good_y);
13591 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13593 int i, kill_x = -1, kill_y = -1;
13594 int bad_element = Feld[bad_x][bad_y];
13595 static int test_xy[4][2] =
13602 static int touch_dir[4] =
13604 MV_LEFT | MV_RIGHT,
13609 static int test_dir[4] =
13617 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
13620 for (i = 0; i < NUM_DIRECTIONS; i++)
13622 int test_x, test_y, test_move_dir, test_element;
13624 test_x = bad_x + test_xy[i][0];
13625 test_y = bad_y + test_xy[i][1];
13626 if (!IN_LEV_FIELD(test_x, test_y))
13630 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13632 test_element = Feld[test_x][test_y];
13634 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13635 2nd case: DONT_TOUCH style bad thing does not move away from good thing
13637 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
13638 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
13640 /* good thing is player or penguin that does not move away */
13641 if (IS_PLAYER(test_x, test_y))
13643 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13645 if (bad_element == EL_ROBOT && player->is_moving)
13646 continue; /* robot does not kill player if he is moving */
13648 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13650 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13651 continue; /* center and border element do not touch */
13658 else if (test_element == EL_PENGUIN)
13667 if (kill_x != -1 || kill_y != -1)
13669 if (IS_PLAYER(kill_x, kill_y))
13671 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13673 if (player->shield_deadly_time_left > 0 &&
13674 !IS_INDESTRUCTIBLE(bad_element))
13675 Bang(bad_x, bad_y);
13676 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13677 KillPlayer(player);
13680 Bang(kill_x, kill_y);
13684 void TestIfPlayerTouchesBadThing(int x, int y)
13686 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13689 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13691 TestIfGoodThingHitsBadThing(x, y, move_dir);
13694 void TestIfBadThingTouchesPlayer(int x, int y)
13696 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13699 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13701 TestIfBadThingHitsGoodThing(x, y, move_dir);
13704 void TestIfFriendTouchesBadThing(int x, int y)
13706 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13709 void TestIfBadThingTouchesFriend(int x, int y)
13711 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13714 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13716 int i, kill_x = bad_x, kill_y = bad_y;
13717 static int xy[4][2] =
13725 for (i = 0; i < NUM_DIRECTIONS; i++)
13729 x = bad_x + xy[i][0];
13730 y = bad_y + xy[i][1];
13731 if (!IN_LEV_FIELD(x, y))
13734 element = Feld[x][y];
13735 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13736 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13744 if (kill_x != bad_x || kill_y != bad_y)
13745 Bang(bad_x, bad_y);
13748 void KillPlayer(struct PlayerInfo *player)
13750 int jx = player->jx, jy = player->jy;
13752 if (!player->active)
13755 /* the following code was introduced to prevent an infinite loop when calling
13757 -> CheckTriggeredElementChangeExt()
13758 -> ExecuteCustomElementAction()
13760 -> (infinitely repeating the above sequence of function calls)
13761 which occurs when killing the player while having a CE with the setting
13762 "kill player X when explosion of <player X>"; the solution using a new
13763 field "player->killed" was chosen for backwards compatibility, although
13764 clever use of the fields "player->active" etc. would probably also work */
13766 if (player->killed)
13770 player->killed = TRUE;
13772 /* remove accessible field at the player's position */
13773 Feld[jx][jy] = EL_EMPTY;
13775 /* deactivate shield (else Bang()/Explode() would not work right) */
13776 player->shield_normal_time_left = 0;
13777 player->shield_deadly_time_left = 0;
13780 BuryPlayer(player);
13783 static void KillPlayerUnlessEnemyProtected(int x, int y)
13785 if (!PLAYER_ENEMY_PROTECTED(x, y))
13786 KillPlayer(PLAYERINFO(x, y));
13789 static void KillPlayerUnlessExplosionProtected(int x, int y)
13791 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13792 KillPlayer(PLAYERINFO(x, y));
13795 void BuryPlayer(struct PlayerInfo *player)
13797 int jx = player->jx, jy = player->jy;
13799 if (!player->active)
13802 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13803 PlayLevelSound(jx, jy, SND_GAME_LOSING);
13805 player->GameOver = TRUE;
13806 RemovePlayer(player);
13809 void RemovePlayer(struct PlayerInfo *player)
13811 int jx = player->jx, jy = player->jy;
13812 int i, found = FALSE;
13814 player->present = FALSE;
13815 player->active = FALSE;
13817 if (!ExplodeField[jx][jy])
13818 StorePlayer[jx][jy] = 0;
13820 if (player->is_moving)
13821 DrawLevelField(player->last_jx, player->last_jy);
13823 for (i = 0; i < MAX_PLAYERS; i++)
13824 if (stored_player[i].active)
13828 AllPlayersGone = TRUE;
13834 #if USE_NEW_SNAP_DELAY
13835 static void setFieldForSnapping(int x, int y, int element, int direction)
13837 struct ElementInfo *ei = &element_info[element];
13838 int direction_bit = MV_DIR_TO_BIT(direction);
13839 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13840 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13841 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13843 Feld[x][y] = EL_ELEMENT_SNAPPING;
13844 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13846 ResetGfxAnimation(x, y);
13848 GfxElement[x][y] = element;
13849 GfxAction[x][y] = action;
13850 GfxDir[x][y] = direction;
13851 GfxFrame[x][y] = -1;
13856 =============================================================================
13857 checkDiagonalPushing()
13858 -----------------------------------------------------------------------------
13859 check if diagonal input device direction results in pushing of object
13860 (by checking if the alternative direction is walkable, diggable, ...)
13861 =============================================================================
13864 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13865 int x, int y, int real_dx, int real_dy)
13867 int jx, jy, dx, dy, xx, yy;
13869 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
13872 /* diagonal direction: check alternative direction */
13877 xx = jx + (dx == 0 ? real_dx : 0);
13878 yy = jy + (dy == 0 ? real_dy : 0);
13880 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13884 =============================================================================
13886 -----------------------------------------------------------------------------
13887 x, y: field next to player (non-diagonal) to try to dig to
13888 real_dx, real_dy: direction as read from input device (can be diagonal)
13889 =============================================================================
13892 int DigField(struct PlayerInfo *player,
13893 int oldx, int oldy, int x, int y,
13894 int real_dx, int real_dy, int mode)
13896 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13897 boolean player_was_pushing = player->is_pushing;
13898 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13899 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13900 int jx = oldx, jy = oldy;
13901 int dx = x - jx, dy = y - jy;
13902 int nextx = x + dx, nexty = y + dy;
13903 int move_direction = (dx == -1 ? MV_LEFT :
13904 dx == +1 ? MV_RIGHT :
13906 dy == +1 ? MV_DOWN : MV_NONE);
13907 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13908 int dig_side = MV_DIR_OPPOSITE(move_direction);
13909 int old_element = Feld[jx][jy];
13910 #if USE_FIXED_DONT_RUN_INTO
13911 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13917 if (is_player) /* function can also be called by EL_PENGUIN */
13919 if (player->MovPos == 0)
13921 player->is_digging = FALSE;
13922 player->is_collecting = FALSE;
13925 if (player->MovPos == 0) /* last pushing move finished */
13926 player->is_pushing = FALSE;
13928 if (mode == DF_NO_PUSH) /* player just stopped pushing */
13930 player->is_switching = FALSE;
13931 player->push_delay = -1;
13933 return MP_NO_ACTION;
13937 #if !USE_FIXED_DONT_RUN_INTO
13938 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13939 return MP_NO_ACTION;
13942 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13943 old_element = Back[jx][jy];
13945 /* in case of element dropped at player position, check background */
13946 else if (Back[jx][jy] != EL_EMPTY &&
13947 game.engine_version >= VERSION_IDENT(2,2,0,0))
13948 old_element = Back[jx][jy];
13950 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13951 return MP_NO_ACTION; /* field has no opening in this direction */
13953 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13954 return MP_NO_ACTION; /* field has no opening in this direction */
13956 #if USE_FIXED_DONT_RUN_INTO
13957 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13961 Feld[jx][jy] = player->artwork_element;
13962 InitMovingField(jx, jy, MV_DOWN);
13963 Store[jx][jy] = EL_ACID;
13964 ContinueMoving(jx, jy);
13965 BuryPlayer(player);
13967 return MP_DONT_RUN_INTO;
13971 #if USE_FIXED_DONT_RUN_INTO
13972 if (player_can_move && DONT_RUN_INTO(element))
13974 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13976 return MP_DONT_RUN_INTO;
13980 #if USE_FIXED_DONT_RUN_INTO
13981 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13982 return MP_NO_ACTION;
13985 #if !USE_FIXED_DONT_RUN_INTO
13986 element = Feld[x][y];
13989 collect_count = element_info[element].collect_count_initial;
13991 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
13992 return MP_NO_ACTION;
13994 if (game.engine_version < VERSION_IDENT(2,2,0,0))
13995 player_can_move = player_can_move_or_snap;
13997 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13998 game.engine_version >= VERSION_IDENT(2,2,0,0))
14000 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14001 player->index_bit, dig_side);
14002 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14003 player->index_bit, dig_side);
14005 if (element == EL_DC_LANDMINE)
14008 if (Feld[x][y] != element) /* field changed by snapping */
14011 return MP_NO_ACTION;
14014 #if USE_PLAYER_GRAVITY
14015 if (player->gravity && is_player && !player->is_auto_moving &&
14016 canFallDown(player) && move_direction != MV_DOWN &&
14017 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14018 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14020 if (game.gravity && is_player && !player->is_auto_moving &&
14021 canFallDown(player) && move_direction != MV_DOWN &&
14022 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14023 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14026 if (player_can_move &&
14027 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14029 int sound_element = SND_ELEMENT(element);
14030 int sound_action = ACTION_WALKING;
14032 if (IS_RND_GATE(element))
14034 if (!player->key[RND_GATE_NR(element)])
14035 return MP_NO_ACTION;
14037 else if (IS_RND_GATE_GRAY(element))
14039 if (!player->key[RND_GATE_GRAY_NR(element)])
14040 return MP_NO_ACTION;
14042 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14044 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14045 return MP_NO_ACTION;
14047 else if (element == EL_EXIT_OPEN ||
14048 element == EL_EM_EXIT_OPEN ||
14049 element == EL_STEEL_EXIT_OPEN ||
14050 element == EL_EM_STEEL_EXIT_OPEN ||
14051 element == EL_SP_EXIT_OPEN ||
14052 element == EL_SP_EXIT_OPENING)
14054 sound_action = ACTION_PASSING; /* player is passing exit */
14056 else if (element == EL_EMPTY)
14058 sound_action = ACTION_MOVING; /* nothing to walk on */
14061 /* play sound from background or player, whatever is available */
14062 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14063 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14065 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14067 else if (player_can_move &&
14068 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14070 if (!ACCESS_FROM(element, opposite_direction))
14071 return MP_NO_ACTION; /* field not accessible from this direction */
14073 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14074 return MP_NO_ACTION;
14076 if (IS_EM_GATE(element))
14078 if (!player->key[EM_GATE_NR(element)])
14079 return MP_NO_ACTION;
14081 else if (IS_EM_GATE_GRAY(element))
14083 if (!player->key[EM_GATE_GRAY_NR(element)])
14084 return MP_NO_ACTION;
14086 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14088 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14089 return MP_NO_ACTION;
14091 else if (IS_EMC_GATE(element))
14093 if (!player->key[EMC_GATE_NR(element)])
14094 return MP_NO_ACTION;
14096 else if (IS_EMC_GATE_GRAY(element))
14098 if (!player->key[EMC_GATE_GRAY_NR(element)])
14099 return MP_NO_ACTION;
14101 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14103 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14104 return MP_NO_ACTION;
14106 else if (element == EL_DC_GATE_WHITE ||
14107 element == EL_DC_GATE_WHITE_GRAY ||
14108 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14110 if (player->num_white_keys == 0)
14111 return MP_NO_ACTION;
14113 player->num_white_keys--;
14115 else if (IS_SP_PORT(element))
14117 if (element == EL_SP_GRAVITY_PORT_LEFT ||
14118 element == EL_SP_GRAVITY_PORT_RIGHT ||
14119 element == EL_SP_GRAVITY_PORT_UP ||
14120 element == EL_SP_GRAVITY_PORT_DOWN)
14121 #if USE_PLAYER_GRAVITY
14122 player->gravity = !player->gravity;
14124 game.gravity = !game.gravity;
14126 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14127 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14128 element == EL_SP_GRAVITY_ON_PORT_UP ||
14129 element == EL_SP_GRAVITY_ON_PORT_DOWN)
14130 #if USE_PLAYER_GRAVITY
14131 player->gravity = TRUE;
14133 game.gravity = TRUE;
14135 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14136 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14137 element == EL_SP_GRAVITY_OFF_PORT_UP ||
14138 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14139 #if USE_PLAYER_GRAVITY
14140 player->gravity = FALSE;
14142 game.gravity = FALSE;
14146 /* automatically move to the next field with double speed */
14147 player->programmed_action = move_direction;
14149 if (player->move_delay_reset_counter == 0)
14151 player->move_delay_reset_counter = 2; /* two double speed steps */
14153 DOUBLE_PLAYER_SPEED(player);
14156 PlayLevelSoundAction(x, y, ACTION_PASSING);
14158 else if (player_can_move_or_snap && IS_DIGGABLE(element))
14162 if (mode != DF_SNAP)
14164 GfxElement[x][y] = GFX_ELEMENT(element);
14165 player->is_digging = TRUE;
14168 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14170 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14171 player->index_bit, dig_side);
14173 if (mode == DF_SNAP)
14175 #if USE_NEW_SNAP_DELAY
14176 if (level.block_snap_field)
14177 setFieldForSnapping(x, y, element, move_direction);
14179 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14181 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14184 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14185 player->index_bit, dig_side);
14188 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14192 if (is_player && mode != DF_SNAP)
14194 GfxElement[x][y] = element;
14195 player->is_collecting = TRUE;
14198 if (element == EL_SPEED_PILL)
14200 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14202 else if (element == EL_EXTRA_TIME && level.time > 0)
14204 TimeLeft += level.extra_time;
14207 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14209 DisplayGameControlValues();
14211 DrawGameValue_Time(TimeLeft);
14214 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14216 player->shield_normal_time_left += level.shield_normal_time;
14217 if (element == EL_SHIELD_DEADLY)
14218 player->shield_deadly_time_left += level.shield_deadly_time;
14220 else if (element == EL_DYNAMITE ||
14221 element == EL_EM_DYNAMITE ||
14222 element == EL_SP_DISK_RED)
14224 if (player->inventory_size < MAX_INVENTORY_SIZE)
14225 player->inventory_element[player->inventory_size++] = element;
14227 DrawGameDoorValues();
14229 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14231 player->dynabomb_count++;
14232 player->dynabombs_left++;
14234 else if (element == EL_DYNABOMB_INCREASE_SIZE)
14236 player->dynabomb_size++;
14238 else if (element == EL_DYNABOMB_INCREASE_POWER)
14240 player->dynabomb_xl = TRUE;
14242 else if (IS_KEY(element))
14244 player->key[KEY_NR(element)] = TRUE;
14246 DrawGameDoorValues();
14248 else if (element == EL_DC_KEY_WHITE)
14250 player->num_white_keys++;
14252 /* display white keys? */
14253 /* DrawGameDoorValues(); */
14255 else if (IS_ENVELOPE(element))
14257 player->show_envelope = element;
14259 else if (element == EL_EMC_LENSES)
14261 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14263 RedrawAllInvisibleElementsForLenses();
14265 else if (element == EL_EMC_MAGNIFIER)
14267 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14269 RedrawAllInvisibleElementsForMagnifier();
14271 else if (IS_DROPPABLE(element) ||
14272 IS_THROWABLE(element)) /* can be collected and dropped */
14276 if (collect_count == 0)
14277 player->inventory_infinite_element = element;
14279 for (i = 0; i < collect_count; i++)
14280 if (player->inventory_size < MAX_INVENTORY_SIZE)
14281 player->inventory_element[player->inventory_size++] = element;
14283 DrawGameDoorValues();
14285 else if (collect_count > 0)
14287 local_player->gems_still_needed -= collect_count;
14288 if (local_player->gems_still_needed < 0)
14289 local_player->gems_still_needed = 0;
14292 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14294 DisplayGameControlValues();
14296 DrawGameValue_Emeralds(local_player->gems_still_needed);
14300 RaiseScoreElement(element);
14301 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14304 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14305 player->index_bit, dig_side);
14307 if (mode == DF_SNAP)
14309 #if USE_NEW_SNAP_DELAY
14310 if (level.block_snap_field)
14311 setFieldForSnapping(x, y, element, move_direction);
14313 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14315 TestIfElementTouchesCustomElement(x, y); /* for empty space */
14318 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14319 player->index_bit, dig_side);
14322 else if (player_can_move_or_snap && IS_PUSHABLE(element))
14324 if (mode == DF_SNAP && element != EL_BD_ROCK)
14325 return MP_NO_ACTION;
14327 if (CAN_FALL(element) && dy)
14328 return MP_NO_ACTION;
14330 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14331 !(element == EL_SPRING && level.use_spring_bug))
14332 return MP_NO_ACTION;
14334 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14335 ((move_direction & MV_VERTICAL &&
14336 ((element_info[element].move_pattern & MV_LEFT &&
14337 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14338 (element_info[element].move_pattern & MV_RIGHT &&
14339 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14340 (move_direction & MV_HORIZONTAL &&
14341 ((element_info[element].move_pattern & MV_UP &&
14342 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14343 (element_info[element].move_pattern & MV_DOWN &&
14344 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14345 return MP_NO_ACTION;
14347 /* do not push elements already moving away faster than player */
14348 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14349 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14350 return MP_NO_ACTION;
14352 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14354 if (player->push_delay_value == -1 || !player_was_pushing)
14355 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14357 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14359 if (player->push_delay_value == -1)
14360 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14362 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14364 if (!player->is_pushing)
14365 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14368 player->is_pushing = TRUE;
14369 player->is_active = TRUE;
14371 if (!(IN_LEV_FIELD(nextx, nexty) &&
14372 (IS_FREE(nextx, nexty) ||
14373 (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14374 IS_SB_ELEMENT(element)))))
14375 return MP_NO_ACTION;
14377 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14378 return MP_NO_ACTION;
14380 if (player->push_delay == -1) /* new pushing; restart delay */
14381 player->push_delay = 0;
14383 if (player->push_delay < player->push_delay_value &&
14384 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14385 element != EL_SPRING && element != EL_BALLOON)
14387 /* make sure that there is no move delay before next try to push */
14388 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14389 player->move_delay = 0;
14391 return MP_NO_ACTION;
14394 if (IS_SB_ELEMENT(element))
14396 if (element == EL_SOKOBAN_FIELD_FULL)
14398 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14399 local_player->sokobanfields_still_needed++;
14402 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14404 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14405 local_player->sokobanfields_still_needed--;
14408 Feld[x][y] = EL_SOKOBAN_OBJECT;
14410 if (Back[x][y] == Back[nextx][nexty])
14411 PlayLevelSoundAction(x, y, ACTION_PUSHING);
14412 else if (Back[x][y] != 0)
14413 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14416 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14419 if (local_player->sokobanfields_still_needed == 0 &&
14420 game.emulation == EMU_SOKOBAN)
14422 PlayerWins(player);
14424 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14428 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14430 InitMovingField(x, y, move_direction);
14431 GfxAction[x][y] = ACTION_PUSHING;
14433 if (mode == DF_SNAP)
14434 ContinueMoving(x, y);
14436 MovPos[x][y] = (dx != 0 ? dx : dy);
14438 Pushed[x][y] = TRUE;
14439 Pushed[nextx][nexty] = TRUE;
14441 if (game.engine_version < VERSION_IDENT(2,2,0,7))
14442 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14444 player->push_delay_value = -1; /* get new value later */
14446 /* check for element change _after_ element has been pushed */
14447 if (game.use_change_when_pushing_bug)
14449 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14450 player->index_bit, dig_side);
14451 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14452 player->index_bit, dig_side);
14455 else if (IS_SWITCHABLE(element))
14457 if (PLAYER_SWITCHING(player, x, y))
14459 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14460 player->index_bit, dig_side);
14465 player->is_switching = TRUE;
14466 player->switch_x = x;
14467 player->switch_y = y;
14469 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14471 if (element == EL_ROBOT_WHEEL)
14473 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14477 game.robot_wheel_active = TRUE;
14479 DrawLevelField(x, y);
14481 else if (element == EL_SP_TERMINAL)
14485 SCAN_PLAYFIELD(xx, yy)
14487 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14489 else if (Feld[xx][yy] == EL_SP_TERMINAL)
14490 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14493 else if (IS_BELT_SWITCH(element))
14495 ToggleBeltSwitch(x, y);
14497 else if (element == EL_SWITCHGATE_SWITCH_UP ||
14498 element == EL_SWITCHGATE_SWITCH_DOWN ||
14499 element == EL_DC_SWITCHGATE_SWITCH_UP ||
14500 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14502 ToggleSwitchgateSwitch(x, y);
14504 else if (element == EL_LIGHT_SWITCH ||
14505 element == EL_LIGHT_SWITCH_ACTIVE)
14507 ToggleLightSwitch(x, y);
14509 else if (element == EL_TIMEGATE_SWITCH ||
14510 element == EL_DC_TIMEGATE_SWITCH)
14512 ActivateTimegateSwitch(x, y);
14514 else if (element == EL_BALLOON_SWITCH_LEFT ||
14515 element == EL_BALLOON_SWITCH_RIGHT ||
14516 element == EL_BALLOON_SWITCH_UP ||
14517 element == EL_BALLOON_SWITCH_DOWN ||
14518 element == EL_BALLOON_SWITCH_NONE ||
14519 element == EL_BALLOON_SWITCH_ANY)
14521 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
14522 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14523 element == EL_BALLOON_SWITCH_UP ? MV_UP :
14524 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
14525 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
14528 else if (element == EL_LAMP)
14530 Feld[x][y] = EL_LAMP_ACTIVE;
14531 local_player->lights_still_needed--;
14533 ResetGfxAnimation(x, y);
14534 DrawLevelField(x, y);
14536 else if (element == EL_TIME_ORB_FULL)
14538 Feld[x][y] = EL_TIME_ORB_EMPTY;
14540 if (level.time > 0 || level.use_time_orb_bug)
14542 TimeLeft += level.time_orb_time;
14545 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14547 DisplayGameControlValues();
14549 DrawGameValue_Time(TimeLeft);
14553 ResetGfxAnimation(x, y);
14554 DrawLevelField(x, y);
14556 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14557 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14561 game.ball_state = !game.ball_state;
14563 SCAN_PLAYFIELD(xx, yy)
14565 int e = Feld[xx][yy];
14567 if (game.ball_state)
14569 if (e == EL_EMC_MAGIC_BALL)
14570 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14571 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14572 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14576 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14577 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14578 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14579 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14584 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14585 player->index_bit, dig_side);
14587 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14588 player->index_bit, dig_side);
14590 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14591 player->index_bit, dig_side);
14597 if (!PLAYER_SWITCHING(player, x, y))
14599 player->is_switching = TRUE;
14600 player->switch_x = x;
14601 player->switch_y = y;
14603 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14604 player->index_bit, dig_side);
14605 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14606 player->index_bit, dig_side);
14608 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14609 player->index_bit, dig_side);
14610 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14611 player->index_bit, dig_side);
14614 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14615 player->index_bit, dig_side);
14616 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14617 player->index_bit, dig_side);
14619 return MP_NO_ACTION;
14622 player->push_delay = -1;
14624 if (is_player) /* function can also be called by EL_PENGUIN */
14626 if (Feld[x][y] != element) /* really digged/collected something */
14628 player->is_collecting = !player->is_digging;
14629 player->is_active = TRUE;
14636 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14638 int jx = player->jx, jy = player->jy;
14639 int x = jx + dx, y = jy + dy;
14640 int snap_direction = (dx == -1 ? MV_LEFT :
14641 dx == +1 ? MV_RIGHT :
14643 dy == +1 ? MV_DOWN : MV_NONE);
14644 boolean can_continue_snapping = (level.continuous_snapping &&
14645 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14647 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14650 if (!player->active || !IN_LEV_FIELD(x, y))
14658 if (player->MovPos == 0)
14659 player->is_pushing = FALSE;
14661 player->is_snapping = FALSE;
14663 if (player->MovPos == 0)
14665 player->is_moving = FALSE;
14666 player->is_digging = FALSE;
14667 player->is_collecting = FALSE;
14673 #if USE_NEW_CONTINUOUS_SNAPPING
14674 /* prevent snapping with already pressed snap key when not allowed */
14675 if (player->is_snapping && !can_continue_snapping)
14678 if (player->is_snapping)
14682 player->MovDir = snap_direction;
14684 if (player->MovPos == 0)
14686 player->is_moving = FALSE;
14687 player->is_digging = FALSE;
14688 player->is_collecting = FALSE;
14691 player->is_dropping = FALSE;
14692 player->is_dropping_pressed = FALSE;
14693 player->drop_pressed_delay = 0;
14695 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14698 player->is_snapping = TRUE;
14699 player->is_active = TRUE;
14701 if (player->MovPos == 0)
14703 player->is_moving = FALSE;
14704 player->is_digging = FALSE;
14705 player->is_collecting = FALSE;
14708 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
14709 DrawLevelField(player->last_jx, player->last_jy);
14711 DrawLevelField(x, y);
14716 boolean DropElement(struct PlayerInfo *player)
14718 int old_element, new_element;
14719 int dropx = player->jx, dropy = player->jy;
14720 int drop_direction = player->MovDir;
14721 int drop_side = drop_direction;
14723 int drop_element = get_next_dropped_element(player);
14725 int drop_element = (player->inventory_size > 0 ?
14726 player->inventory_element[player->inventory_size - 1] :
14727 player->inventory_infinite_element != EL_UNDEFINED ?
14728 player->inventory_infinite_element :
14729 player->dynabombs_left > 0 ?
14730 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14734 player->is_dropping_pressed = TRUE;
14736 /* do not drop an element on top of another element; when holding drop key
14737 pressed without moving, dropped element must move away before the next
14738 element can be dropped (this is especially important if the next element
14739 is dynamite, which can be placed on background for historical reasons) */
14740 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14743 if (IS_THROWABLE(drop_element))
14745 dropx += GET_DX_FROM_DIR(drop_direction);
14746 dropy += GET_DY_FROM_DIR(drop_direction);
14748 if (!IN_LEV_FIELD(dropx, dropy))
14752 old_element = Feld[dropx][dropy]; /* old element at dropping position */
14753 new_element = drop_element; /* default: no change when dropping */
14755 /* check if player is active, not moving and ready to drop */
14756 if (!player->active || player->MovPos || player->drop_delay > 0)
14759 /* check if player has anything that can be dropped */
14760 if (new_element == EL_UNDEFINED)
14763 /* check if drop key was pressed long enough for EM style dynamite */
14764 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14767 /* check if anything can be dropped at the current position */
14768 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14771 /* collected custom elements can only be dropped on empty fields */
14772 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14775 if (old_element != EL_EMPTY)
14776 Back[dropx][dropy] = old_element; /* store old element on this field */
14778 ResetGfxAnimation(dropx, dropy);
14779 ResetRandomAnimationValue(dropx, dropy);
14781 if (player->inventory_size > 0 ||
14782 player->inventory_infinite_element != EL_UNDEFINED)
14784 if (player->inventory_size > 0)
14786 player->inventory_size--;
14788 DrawGameDoorValues();
14790 if (new_element == EL_DYNAMITE)
14791 new_element = EL_DYNAMITE_ACTIVE;
14792 else if (new_element == EL_EM_DYNAMITE)
14793 new_element = EL_EM_DYNAMITE_ACTIVE;
14794 else if (new_element == EL_SP_DISK_RED)
14795 new_element = EL_SP_DISK_RED_ACTIVE;
14798 Feld[dropx][dropy] = new_element;
14800 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14801 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14802 el2img(Feld[dropx][dropy]), 0);
14804 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14806 /* needed if previous element just changed to "empty" in the last frame */
14807 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14809 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14810 player->index_bit, drop_side);
14811 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14813 player->index_bit, drop_side);
14815 TestIfElementTouchesCustomElement(dropx, dropy);
14817 else /* player is dropping a dyna bomb */
14819 player->dynabombs_left--;
14821 Feld[dropx][dropy] = new_element;
14823 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14824 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14825 el2img(Feld[dropx][dropy]), 0);
14827 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14830 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14831 InitField_WithBug1(dropx, dropy, FALSE);
14833 new_element = Feld[dropx][dropy]; /* element might have changed */
14835 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14836 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14838 int move_direction, nextx, nexty;
14840 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14841 MovDir[dropx][dropy] = drop_direction;
14843 move_direction = MovDir[dropx][dropy];
14844 nextx = dropx + GET_DX_FROM_DIR(move_direction);
14845 nexty = dropy + GET_DY_FROM_DIR(move_direction);
14847 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
14849 #if USE_FIX_IMPACT_COLLISION
14850 /* do not cause impact style collision by dropping elements that can fall */
14851 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14853 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14857 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14858 player->is_dropping = TRUE;
14860 player->drop_pressed_delay = 0;
14861 player->is_dropping_pressed = FALSE;
14863 player->drop_x = dropx;
14864 player->drop_y = dropy;
14869 /* ------------------------------------------------------------------------- */
14870 /* game sound playing functions */
14871 /* ------------------------------------------------------------------------- */
14873 static int *loop_sound_frame = NULL;
14874 static int *loop_sound_volume = NULL;
14876 void InitPlayLevelSound()
14878 int num_sounds = getSoundListSize();
14880 checked_free(loop_sound_frame);
14881 checked_free(loop_sound_volume);
14883 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
14884 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14887 static void PlayLevelSound(int x, int y, int nr)
14889 int sx = SCREENX(x), sy = SCREENY(y);
14890 int volume, stereo_position;
14891 int max_distance = 8;
14892 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14894 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14895 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14898 if (!IN_LEV_FIELD(x, y) ||
14899 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14900 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14903 volume = SOUND_MAX_VOLUME;
14905 if (!IN_SCR_FIELD(sx, sy))
14907 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14908 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14910 volume -= volume * (dx > dy ? dx : dy) / max_distance;
14913 stereo_position = (SOUND_MAX_LEFT +
14914 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14915 (SCR_FIELDX + 2 * max_distance));
14917 if (IS_LOOP_SOUND(nr))
14919 /* This assures that quieter loop sounds do not overwrite louder ones,
14920 while restarting sound volume comparison with each new game frame. */
14922 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14925 loop_sound_volume[nr] = volume;
14926 loop_sound_frame[nr] = FrameCounter;
14929 PlaySoundExt(nr, volume, stereo_position, type);
14932 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14934 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14935 x > LEVELX(BX2) ? LEVELX(BX2) : x,
14936 y < LEVELY(BY1) ? LEVELY(BY1) :
14937 y > LEVELY(BY2) ? LEVELY(BY2) : y,
14941 static void PlayLevelSoundAction(int x, int y, int action)
14943 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14946 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14948 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14950 if (sound_effect != SND_UNDEFINED)
14951 PlayLevelSound(x, y, sound_effect);
14954 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14957 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14959 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14960 PlayLevelSound(x, y, sound_effect);
14963 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14965 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14967 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14968 PlayLevelSound(x, y, sound_effect);
14971 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14973 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14975 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14976 StopSound(sound_effect);
14979 static void PlayLevelMusic()
14981 if (levelset.music[level_nr] != MUS_UNDEFINED)
14982 PlayMusic(levelset.music[level_nr]); /* from config file */
14984 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
14987 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14989 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14990 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14991 int x = xx - 1 - offset;
14992 int y = yy - 1 - offset;
14997 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15001 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15005 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15009 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15013 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15017 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15021 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15024 case SAMPLE_android_clone:
15025 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15028 case SAMPLE_android_move:
15029 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15032 case SAMPLE_spring:
15033 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15037 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15041 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15044 case SAMPLE_eater_eat:
15045 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15049 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15052 case SAMPLE_collect:
15053 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15056 case SAMPLE_diamond:
15057 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15060 case SAMPLE_squash:
15061 /* !!! CHECK THIS !!! */
15063 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15065 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15069 case SAMPLE_wonderfall:
15070 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15074 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15078 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15082 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15086 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15090 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15094 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15097 case SAMPLE_wonder:
15098 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15102 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15105 case SAMPLE_exit_open:
15106 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15109 case SAMPLE_exit_leave:
15110 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15113 case SAMPLE_dynamite:
15114 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15118 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15122 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15126 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15130 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15134 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15138 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15142 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15148 void ChangeTime(int value)
15150 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15154 /* EMC game engine uses value from time counter of RND game engine */
15155 level.native_em_level->lev->time = *time;
15157 DrawGameValue_Time(*time);
15160 void RaiseScore(int value)
15162 /* EMC game engine and RND game engine have separate score counters */
15163 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15164 &level.native_em_level->lev->score : &local_player->score);
15168 DrawGameValue_Score(*score);
15172 void RaiseScore(int value)
15174 local_player->score += value;
15177 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15179 DisplayGameControlValues();
15181 DrawGameValue_Score(local_player->score);
15185 void RaiseScoreElement(int element)
15190 case EL_BD_DIAMOND:
15191 case EL_EMERALD_YELLOW:
15192 case EL_EMERALD_RED:
15193 case EL_EMERALD_PURPLE:
15194 case EL_SP_INFOTRON:
15195 RaiseScore(level.score[SC_EMERALD]);
15198 RaiseScore(level.score[SC_DIAMOND]);
15201 RaiseScore(level.score[SC_CRYSTAL]);
15204 RaiseScore(level.score[SC_PEARL]);
15207 case EL_BD_BUTTERFLY:
15208 case EL_SP_ELECTRON:
15209 RaiseScore(level.score[SC_BUG]);
15212 case EL_BD_FIREFLY:
15213 case EL_SP_SNIKSNAK:
15214 RaiseScore(level.score[SC_SPACESHIP]);
15217 case EL_DARK_YAMYAM:
15218 RaiseScore(level.score[SC_YAMYAM]);
15221 RaiseScore(level.score[SC_ROBOT]);
15224 RaiseScore(level.score[SC_PACMAN]);
15227 RaiseScore(level.score[SC_NUT]);
15230 case EL_EM_DYNAMITE:
15231 case EL_SP_DISK_RED:
15232 case EL_DYNABOMB_INCREASE_NUMBER:
15233 case EL_DYNABOMB_INCREASE_SIZE:
15234 case EL_DYNABOMB_INCREASE_POWER:
15235 RaiseScore(level.score[SC_DYNAMITE]);
15237 case EL_SHIELD_NORMAL:
15238 case EL_SHIELD_DEADLY:
15239 RaiseScore(level.score[SC_SHIELD]);
15241 case EL_EXTRA_TIME:
15242 RaiseScore(level.extra_time_score);
15256 case EL_DC_KEY_WHITE:
15257 RaiseScore(level.score[SC_KEY]);
15260 RaiseScore(element_info[element].collect_score);
15265 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15267 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15269 #if defined(NETWORK_AVALIABLE)
15270 if (options.network)
15271 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15280 FadeSkipNextFadeIn();
15282 fading = fading_none;
15286 OpenDoor(DOOR_CLOSE_1);
15289 game_status = GAME_MODE_MAIN;
15292 DrawAndFadeInMainMenu(REDRAW_FIELD);
15300 FadeOut(REDRAW_FIELD);
15303 game_status = GAME_MODE_MAIN;
15305 DrawAndFadeInMainMenu(REDRAW_FIELD);
15309 else /* continue playing the game */
15311 if (tape.playing && tape.deactivate_display)
15312 TapeDeactivateDisplayOff(TRUE);
15314 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15316 if (tape.playing && tape.deactivate_display)
15317 TapeDeactivateDisplayOn();
15321 void RequestQuitGame(boolean ask_if_really_quit)
15323 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15324 boolean skip_request = AllPlayersGone || quick_quit;
15326 RequestQuitGameExt(skip_request, quick_quit,
15327 "Do you really want to quit the game ?");
15331 /* ------------------------------------------------------------------------- */
15332 /* random generator functions */
15333 /* ------------------------------------------------------------------------- */
15335 unsigned int InitEngineRandom_RND(long seed)
15337 game.num_random_calls = 0;
15340 unsigned int rnd_seed = InitEngineRandom(seed);
15342 printf("::: START RND: %d\n", rnd_seed);
15347 return InitEngineRandom(seed);
15353 unsigned int RND(int max)
15357 game.num_random_calls++;
15359 return GetEngineRandom(max);
15366 /* ------------------------------------------------------------------------- */
15367 /* game engine snapshot handling functions */
15368 /* ------------------------------------------------------------------------- */
15370 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
15372 struct EngineSnapshotInfo
15374 /* runtime values for custom element collect score */
15375 int collect_score[NUM_CUSTOM_ELEMENTS];
15377 /* runtime values for group element choice position */
15378 int choice_pos[NUM_GROUP_ELEMENTS];
15380 /* runtime values for belt position animations */
15381 int belt_graphic[4 * NUM_BELT_PARTS];
15382 int belt_anim_mode[4 * NUM_BELT_PARTS];
15385 struct EngineSnapshotNodeInfo
15392 static struct EngineSnapshotInfo engine_snapshot_rnd;
15393 static ListNode *engine_snapshot_list = NULL;
15394 static char *snapshot_level_identifier = NULL;
15395 static int snapshot_level_nr = -1;
15397 void FreeEngineSnapshot()
15399 while (engine_snapshot_list != NULL)
15400 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15403 setString(&snapshot_level_identifier, NULL);
15404 snapshot_level_nr = -1;
15407 static void SaveEngineSnapshotValues_RND()
15409 static int belt_base_active_element[4] =
15411 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15412 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15413 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15414 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15418 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15420 int element = EL_CUSTOM_START + i;
15422 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15425 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15427 int element = EL_GROUP_START + i;
15429 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15432 for (i = 0; i < 4; i++)
15434 for (j = 0; j < NUM_BELT_PARTS; j++)
15436 int element = belt_base_active_element[i] + j;
15437 int graphic = el2img(element);
15438 int anim_mode = graphic_info[graphic].anim_mode;
15440 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15441 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15446 static void LoadEngineSnapshotValues_RND()
15448 unsigned long num_random_calls = game.num_random_calls;
15451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15453 int element = EL_CUSTOM_START + i;
15455 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15458 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15460 int element = EL_GROUP_START + i;
15462 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15465 for (i = 0; i < 4; i++)
15467 for (j = 0; j < NUM_BELT_PARTS; j++)
15469 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15470 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15472 graphic_info[graphic].anim_mode = anim_mode;
15476 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15478 InitRND(tape.random_seed);
15479 for (i = 0; i < num_random_calls; i++)
15483 if (game.num_random_calls != num_random_calls)
15485 Error(ERR_INFO, "number of random calls out of sync");
15486 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15487 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15488 Error(ERR_EXIT, "this should not happen -- please debug");
15492 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15494 struct EngineSnapshotNodeInfo *bi =
15495 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15497 bi->buffer_orig = buffer;
15498 bi->buffer_copy = checked_malloc(size);
15501 memcpy(bi->buffer_copy, buffer, size);
15503 addNodeToList(&engine_snapshot_list, NULL, bi);
15506 void SaveEngineSnapshot()
15508 FreeEngineSnapshot(); /* free previous snapshot, if needed */
15510 if (level_editor_test_game) /* do not save snapshots from editor */
15513 /* copy some special values to a structure better suited for the snapshot */
15515 SaveEngineSnapshotValues_RND();
15516 SaveEngineSnapshotValues_EM();
15518 /* save values stored in special snapshot structure */
15520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15521 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15523 /* save further RND engine values */
15525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15527 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15532 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15536 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15540 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15549 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15552 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15553 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15554 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15555 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15556 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15557 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15558 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15559 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15560 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15561 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15562 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15563 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15564 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15565 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15566 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15567 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15568 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15570 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15571 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15573 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15574 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15575 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15577 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15578 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15580 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15581 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15582 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15583 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15584 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15586 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15587 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15589 /* save level identification information */
15591 setString(&snapshot_level_identifier, leveldir_current->identifier);
15592 snapshot_level_nr = level_nr;
15595 ListNode *node = engine_snapshot_list;
15598 while (node != NULL)
15600 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15605 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15609 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15611 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15614 void LoadEngineSnapshot()
15616 ListNode *node = engine_snapshot_list;
15618 if (engine_snapshot_list == NULL)
15621 while (node != NULL)
15623 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15628 /* restore special values from snapshot structure */
15630 LoadEngineSnapshotValues_RND();
15631 LoadEngineSnapshotValues_EM();
15634 boolean CheckEngineSnapshot()
15636 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15637 snapshot_level_nr == level_nr);
15641 /* ---------- new game button stuff ---------------------------------------- */
15643 /* graphic position values for game buttons */
15644 #define GAME_BUTTON_XSIZE 30
15645 #define GAME_BUTTON_YSIZE 30
15646 #define GAME_BUTTON_XPOS 5
15647 #define GAME_BUTTON_YPOS 215
15648 #define SOUND_BUTTON_XPOS 5
15649 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15651 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15652 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15653 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15654 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15655 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15656 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15664 } gamebutton_info[NUM_GAME_BUTTONS] =
15668 &game.button.stop.x, &game.button.stop.y,
15669 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15674 &game.button.pause.x, &game.button.pause.y,
15675 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15676 GAME_CTRL_ID_PAUSE,
15680 &game.button.play.x, &game.button.play.y,
15681 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15686 &game.button.sound_music.x, &game.button.sound_music.y,
15687 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15688 SOUND_CTRL_ID_MUSIC,
15689 "background music on/off"
15692 &game.button.sound_loops.x, &game.button.sound_loops.y,
15693 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15694 SOUND_CTRL_ID_LOOPS,
15695 "sound loops on/off"
15698 &game.button.sound_simple.x,&game.button.sound_simple.y,
15699 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15700 SOUND_CTRL_ID_SIMPLE,
15701 "normal sounds on/off"
15705 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
15710 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
15711 GAME_CTRL_ID_PAUSE,
15715 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
15720 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
15721 SOUND_CTRL_ID_MUSIC,
15722 "background music on/off"
15725 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
15726 SOUND_CTRL_ID_LOOPS,
15727 "sound loops on/off"
15730 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
15731 SOUND_CTRL_ID_SIMPLE,
15732 "normal sounds on/off"
15737 void CreateGameButtons()
15741 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15743 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15744 struct GadgetInfo *gi;
15747 unsigned long event_mask;
15749 int gd_xoffset, gd_yoffset;
15750 int gd_x1, gd_x2, gd_y1, gd_y2;
15753 x = DX + *gamebutton_info[i].x;
15754 y = DY + *gamebutton_info[i].y;
15755 gd_xoffset = gamebutton_info[i].gd_x;
15756 gd_yoffset = gamebutton_info[i].gd_y;
15757 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15758 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15760 if (id == GAME_CTRL_ID_STOP ||
15761 id == GAME_CTRL_ID_PAUSE ||
15762 id == GAME_CTRL_ID_PLAY)
15764 button_type = GD_TYPE_NORMAL_BUTTON;
15766 event_mask = GD_EVENT_RELEASED;
15767 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15768 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15772 button_type = GD_TYPE_CHECK_BUTTON;
15774 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15775 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15776 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15777 event_mask = GD_EVENT_PRESSED;
15778 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
15779 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15782 gi = CreateGadget(GDI_CUSTOM_ID, id,
15783 GDI_INFO_TEXT, gamebutton_info[i].infotext,
15788 GDI_X, DX + gd_xoffset,
15789 GDI_Y, DY + gd_yoffset,
15791 GDI_WIDTH, GAME_BUTTON_XSIZE,
15792 GDI_HEIGHT, GAME_BUTTON_YSIZE,
15793 GDI_TYPE, button_type,
15794 GDI_STATE, GD_BUTTON_UNPRESSED,
15795 GDI_CHECKED, checked,
15796 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15797 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15798 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15799 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15800 GDI_DIRECT_DRAW, FALSE,
15801 GDI_EVENT_MASK, event_mask,
15802 GDI_CALLBACK_ACTION, HandleGameButtons,
15806 Error(ERR_EXIT, "cannot create gadget");
15808 game_gadget[id] = gi;
15812 void FreeGameButtons()
15816 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15817 FreeGadget(game_gadget[i]);
15820 static void MapGameButtons()
15824 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15825 MapGadget(game_gadget[i]);
15828 void UnmapGameButtons()
15832 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15833 UnmapGadget(game_gadget[i]);
15836 void RedrawGameButtons()
15840 for (i = 0; i < NUM_GAME_BUTTONS; i++)
15841 RedrawGadget(game_gadget[i]);
15844 static void HandleGameButtons(struct GadgetInfo *gi)
15846 int id = gi->custom_id;
15848 if (game_status != GAME_MODE_PLAYING)
15853 case GAME_CTRL_ID_STOP:
15857 RequestQuitGame(TRUE);
15860 case GAME_CTRL_ID_PAUSE:
15861 if (options.network)
15863 #if defined(NETWORK_AVALIABLE)
15865 SendToServer_ContinuePlaying();
15867 SendToServer_PausePlaying();
15871 TapeTogglePause(TAPE_TOGGLE_MANUAL);
15874 case GAME_CTRL_ID_PLAY:
15877 #if defined(NETWORK_AVALIABLE)
15878 if (options.network)
15879 SendToServer_ContinuePlaying();
15883 tape.pausing = FALSE;
15884 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15889 case SOUND_CTRL_ID_MUSIC:
15890 if (setup.sound_music)
15892 setup.sound_music = FALSE;
15895 else if (audio.music_available)
15897 setup.sound = setup.sound_music = TRUE;
15899 SetAudioMode(setup.sound);
15905 case SOUND_CTRL_ID_LOOPS:
15906 if (setup.sound_loops)
15907 setup.sound_loops = FALSE;
15908 else if (audio.loops_available)
15910 setup.sound = setup.sound_loops = TRUE;
15911 SetAudioMode(setup.sound);
15915 case SOUND_CTRL_ID_SIMPLE:
15916 if (setup.sound_simple)
15917 setup.sound_simple = FALSE;
15918 else if (audio.sound_available)
15920 setup.sound = setup.sound_simple = TRUE;
15921 SetAudioMode(setup.sound);