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_TARGET_ELEMENT(be, e, ch, cv, cs) \
900 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
901 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
902 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
903 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
904 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
905 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
906 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
907 RESOLVED_REFERENCE_ELEMENT(be, e) : \
910 #define CAN_GROW_INTO(e) \
911 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
913 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
914 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
917 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
918 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
919 (CAN_MOVE_INTO_ACID(e) && \
920 Feld[x][y] == EL_ACID) || \
923 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
924 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
925 (CAN_MOVE_INTO_ACID(e) && \
926 Feld[x][y] == EL_ACID) || \
929 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
930 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
932 (CAN_MOVE_INTO_ACID(e) && \
933 Feld[x][y] == EL_ACID) || \
934 (DONT_COLLIDE_WITH(e) && \
936 !PLAYER_ENEMY_PROTECTED(x, y))))
938 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
939 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
941 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
942 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
944 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
945 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
947 #define ANDROID_CAN_CLONE_FIELD(x, y) \
948 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
949 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
951 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
952 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
954 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
955 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
957 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
958 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
960 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
961 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
963 #define PIG_CAN_ENTER_FIELD(e, x, y) \
964 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
966 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
967 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
968 Feld[x][y] == EL_EM_EXIT_OPEN || \
969 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
970 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
971 IS_FOOD_PENGUIN(Feld[x][y])))
972 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
973 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
975 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
978 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
982 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
983 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
985 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
987 #define CE_ENTER_FIELD_COND(e, x, y) \
988 (!IS_PLAYER(x, y) && \
989 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
991 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
994 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
995 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
997 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
998 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
999 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1000 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1002 /* game button identifiers */
1003 #define GAME_CTRL_ID_STOP 0
1004 #define GAME_CTRL_ID_PAUSE 1
1005 #define GAME_CTRL_ID_PLAY 2
1006 #define SOUND_CTRL_ID_MUSIC 3
1007 #define SOUND_CTRL_ID_LOOPS 4
1008 #define SOUND_CTRL_ID_SIMPLE 5
1010 #define NUM_GAME_BUTTONS 6
1013 /* forward declaration for internal use */
1015 static void CreateField(int, int, int);
1017 static void ResetGfxAnimation(int, int);
1019 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1020 static void AdvanceFrameAndPlayerCounters(int);
1022 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1023 static boolean MovePlayer(struct PlayerInfo *, int, int);
1024 static void ScrollPlayer(struct PlayerInfo *, int);
1025 static void ScrollScreen(struct PlayerInfo *, int);
1027 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1029 static void InitBeltMovement(void);
1030 static void CloseAllOpenTimegates(void);
1031 static void CheckGravityMovement(struct PlayerInfo *);
1032 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1033 static void KillPlayerUnlessEnemyProtected(int, int);
1034 static void KillPlayerUnlessExplosionProtected(int, int);
1036 static void TestIfPlayerTouchesCustomElement(int, int);
1037 static void TestIfElementTouchesCustomElement(int, int);
1038 static void TestIfElementHitsCustomElement(int, int, int);
1040 static void TestIfElementSmashesCustomElement(int, int, int);
1043 static void HandleElementChange(int, int, int);
1044 static void ExecuteCustomElementAction(int, int, int, int);
1045 static boolean ChangeElement(int, int, int, int);
1047 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1048 #define CheckTriggeredElementChange(x, y, e, ev) \
1049 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1050 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1051 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1052 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1053 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1054 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1055 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1057 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1058 #define CheckElementChange(x, y, e, te, ev) \
1059 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1060 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1061 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1062 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1063 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1065 static void PlayLevelSound(int, int, int);
1066 static void PlayLevelSoundNearest(int, int, int);
1067 static void PlayLevelSoundAction(int, int, int);
1068 static void PlayLevelSoundElementAction(int, int, int, int);
1069 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1070 static void PlayLevelSoundActionIfLoop(int, int, int);
1071 static void StopLevelSoundActionIfLoop(int, int, int);
1072 static void PlayLevelMusic();
1074 static void MapGameButtons();
1075 static void HandleGameButtons(struct GadgetInfo *);
1077 int AmoebeNachbarNr(int, int);
1078 void AmoebeUmwandeln(int, int);
1079 void ContinueMoving(int, int);
1080 void Bang(int, int);
1081 void InitMovDir(int, int);
1082 void InitAmoebaNr(int, int);
1083 int NewHiScore(void);
1085 void TestIfGoodThingHitsBadThing(int, int, int);
1086 void TestIfBadThingHitsGoodThing(int, int, int);
1087 void TestIfPlayerTouchesBadThing(int, int);
1088 void TestIfPlayerRunsIntoBadThing(int, int, int);
1089 void TestIfBadThingTouchesPlayer(int, int);
1090 void TestIfBadThingRunsIntoPlayer(int, int, int);
1091 void TestIfFriendTouchesBadThing(int, int);
1092 void TestIfBadThingTouchesFriend(int, int);
1093 void TestIfBadThingTouchesOtherBadThing(int, int);
1095 void KillPlayer(struct PlayerInfo *);
1096 void BuryPlayer(struct PlayerInfo *);
1097 void RemovePlayer(struct PlayerInfo *);
1099 boolean SnapField(struct PlayerInfo *, int, int);
1100 boolean DropElement(struct PlayerInfo *);
1102 static int getInvisibleActiveFromInvisibleElement(int);
1103 static int getInvisibleFromInvisibleActiveElement(int);
1105 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1107 /* for detection of endless loops, caused by custom element programming */
1108 /* (using maximal playfield width x 10 is just a rough approximation) */
1109 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1111 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1113 if (recursion_loop_detected) \
1116 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1118 recursion_loop_detected = TRUE; \
1119 recursion_loop_element = (e); \
1122 recursion_loop_depth++; \
1125 #define RECURSION_LOOP_DETECTION_END() \
1127 recursion_loop_depth--; \
1130 static int recursion_loop_depth;
1131 static boolean recursion_loop_detected;
1132 static boolean recursion_loop_element;
1135 /* ------------------------------------------------------------------------- */
1136 /* definition of elements that automatically change to other elements after */
1137 /* a specified time, eventually calling a function when changing */
1138 /* ------------------------------------------------------------------------- */
1140 /* forward declaration for changer functions */
1141 static void InitBuggyBase(int, int);
1142 static void WarnBuggyBase(int, int);
1144 static void InitTrap(int, int);
1145 static void ActivateTrap(int, int);
1146 static void ChangeActiveTrap(int, int);
1148 static void InitRobotWheel(int, int);
1149 static void RunRobotWheel(int, int);
1150 static void StopRobotWheel(int, int);
1152 static void InitTimegateWheel(int, int);
1153 static void RunTimegateWheel(int, int);
1155 static void InitMagicBallDelay(int, int);
1156 static void ActivateMagicBall(int, int);
1158 struct ChangingElementInfo
1163 void (*pre_change_function)(int x, int y);
1164 void (*change_function)(int x, int y);
1165 void (*post_change_function)(int x, int y);
1168 static struct ChangingElementInfo change_delay_list[] =
1203 EL_STEEL_EXIT_OPENING,
1211 EL_STEEL_EXIT_CLOSING,
1212 EL_STEEL_EXIT_CLOSED,
1239 EL_EM_STEEL_EXIT_OPENING,
1240 EL_EM_STEEL_EXIT_OPEN,
1247 EL_EM_STEEL_EXIT_CLOSING,
1251 EL_EM_STEEL_EXIT_CLOSED,
1275 EL_SWITCHGATE_OPENING,
1283 EL_SWITCHGATE_CLOSING,
1284 EL_SWITCHGATE_CLOSED,
1291 EL_TIMEGATE_OPENING,
1299 EL_TIMEGATE_CLOSING,
1308 EL_ACID_SPLASH_LEFT,
1316 EL_ACID_SPLASH_RIGHT,
1325 EL_SP_BUGGY_BASE_ACTIVATING,
1332 EL_SP_BUGGY_BASE_ACTIVATING,
1333 EL_SP_BUGGY_BASE_ACTIVE,
1340 EL_SP_BUGGY_BASE_ACTIVE,
1364 EL_ROBOT_WHEEL_ACTIVE,
1372 EL_TIMEGATE_SWITCH_ACTIVE,
1380 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1381 EL_DC_TIMEGATE_SWITCH,
1388 EL_EMC_MAGIC_BALL_ACTIVE,
1389 EL_EMC_MAGIC_BALL_ACTIVE,
1396 EL_EMC_SPRING_BUMPER_ACTIVE,
1397 EL_EMC_SPRING_BUMPER,
1404 EL_DIAGONAL_SHRINKING,
1412 EL_DIAGONAL_GROWING,
1433 int push_delay_fixed, push_delay_random;
1437 { EL_SPRING, 0, 0 },
1438 { EL_BALLOON, 0, 0 },
1440 { EL_SOKOBAN_OBJECT, 2, 0 },
1441 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1442 { EL_SATELLITE, 2, 0 },
1443 { EL_SP_DISK_YELLOW, 2, 0 },
1445 { EL_UNDEFINED, 0, 0 },
1453 move_stepsize_list[] =
1455 { EL_AMOEBA_DROP, 2 },
1456 { EL_AMOEBA_DROPPING, 2 },
1457 { EL_QUICKSAND_FILLING, 1 },
1458 { EL_QUICKSAND_EMPTYING, 1 },
1459 { EL_QUICKSAND_FAST_FILLING, 2 },
1460 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1461 { EL_MAGIC_WALL_FILLING, 2 },
1462 { EL_MAGIC_WALL_EMPTYING, 2 },
1463 { EL_BD_MAGIC_WALL_FILLING, 2 },
1464 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1465 { EL_DC_MAGIC_WALL_FILLING, 2 },
1466 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1468 { EL_UNDEFINED, 0 },
1476 collect_count_list[] =
1479 { EL_BD_DIAMOND, 1 },
1480 { EL_EMERALD_YELLOW, 1 },
1481 { EL_EMERALD_RED, 1 },
1482 { EL_EMERALD_PURPLE, 1 },
1484 { EL_SP_INFOTRON, 1 },
1488 { EL_UNDEFINED, 0 },
1496 access_direction_list[] =
1498 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1499 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1500 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1501 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1502 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1503 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1504 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1505 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1506 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1507 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1508 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1510 { EL_SP_PORT_LEFT, MV_RIGHT },
1511 { EL_SP_PORT_RIGHT, MV_LEFT },
1512 { EL_SP_PORT_UP, MV_DOWN },
1513 { EL_SP_PORT_DOWN, MV_UP },
1514 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1515 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1516 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1517 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1518 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1519 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1520 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1521 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1522 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1523 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1524 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1525 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1526 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1527 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1528 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1530 { EL_UNDEFINED, MV_NONE }
1533 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1535 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1536 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1537 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1538 IS_JUST_CHANGING(x, y))
1540 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1542 /* static variables for playfield scan mode (scanning forward or backward) */
1543 static int playfield_scan_start_x = 0;
1544 static int playfield_scan_start_y = 0;
1545 static int playfield_scan_delta_x = 1;
1546 static int playfield_scan_delta_y = 1;
1548 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1549 (y) >= 0 && (y) <= lev_fieldy - 1; \
1550 (y) += playfield_scan_delta_y) \
1551 for ((x) = playfield_scan_start_x; \
1552 (x) >= 0 && (x) <= lev_fieldx - 1; \
1553 (x) += playfield_scan_delta_x)
1556 void DEBUG_SetMaximumDynamite()
1560 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1561 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1562 local_player->inventory_element[local_player->inventory_size++] =
1567 static void InitPlayfieldScanModeVars()
1569 if (game.use_reverse_scan_direction)
1571 playfield_scan_start_x = lev_fieldx - 1;
1572 playfield_scan_start_y = lev_fieldy - 1;
1574 playfield_scan_delta_x = -1;
1575 playfield_scan_delta_y = -1;
1579 playfield_scan_start_x = 0;
1580 playfield_scan_start_y = 0;
1582 playfield_scan_delta_x = 1;
1583 playfield_scan_delta_y = 1;
1587 static void InitPlayfieldScanMode(int mode)
1589 game.use_reverse_scan_direction =
1590 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1592 InitPlayfieldScanModeVars();
1595 static int get_move_delay_from_stepsize(int move_stepsize)
1598 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1600 /* make sure that stepsize value is always a power of 2 */
1601 move_stepsize = (1 << log_2(move_stepsize));
1603 return TILEX / move_stepsize;
1606 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1609 int player_nr = player->index_nr;
1610 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1611 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1613 /* do no immediately change move delay -- the player might just be moving */
1614 player->move_delay_value_next = move_delay;
1616 /* information if player can move must be set separately */
1617 player->cannot_move = cannot_move;
1621 player->move_delay = game.initial_move_delay[player_nr];
1622 player->move_delay_value = game.initial_move_delay_value[player_nr];
1624 player->move_delay_value_next = -1;
1626 player->move_delay_reset_counter = 0;
1630 void GetPlayerConfig()
1632 GameFrameDelay = setup.game_frame_delay;
1634 if (!audio.sound_available)
1635 setup.sound_simple = FALSE;
1637 if (!audio.loops_available)
1638 setup.sound_loops = FALSE;
1640 if (!audio.music_available)
1641 setup.sound_music = FALSE;
1643 if (!video.fullscreen_available)
1644 setup.fullscreen = FALSE;
1646 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1648 SetAudioMode(setup.sound);
1652 int GetElementFromGroupElement(int element)
1654 if (IS_GROUP_ELEMENT(element))
1656 struct ElementGroupInfo *group = element_info[element].group;
1657 int last_anim_random_frame = gfx.anim_random_frame;
1660 if (group->choice_mode == ANIM_RANDOM)
1661 gfx.anim_random_frame = RND(group->num_elements_resolved);
1663 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1664 group->choice_mode, 0,
1667 if (group->choice_mode == ANIM_RANDOM)
1668 gfx.anim_random_frame = last_anim_random_frame;
1670 group->choice_pos++;
1672 element = group->element_resolved[element_pos];
1678 static void InitPlayerField(int x, int y, int element, boolean init_game)
1680 if (element == EL_SP_MURPHY)
1684 if (stored_player[0].present)
1686 Feld[x][y] = EL_SP_MURPHY_CLONE;
1692 stored_player[0].use_murphy = TRUE;
1694 if (!level.use_artwork_element[0])
1695 stored_player[0].artwork_element = EL_SP_MURPHY;
1698 Feld[x][y] = EL_PLAYER_1;
1704 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1705 int jx = player->jx, jy = player->jy;
1707 player->present = TRUE;
1709 player->block_last_field = (element == EL_SP_MURPHY ?
1710 level.sp_block_last_field :
1711 level.block_last_field);
1713 /* ---------- initialize player's last field block delay --------------- */
1715 /* always start with reliable default value (no adjustment needed) */
1716 player->block_delay_adjustment = 0;
1718 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1719 if (player->block_last_field && element == EL_SP_MURPHY)
1720 player->block_delay_adjustment = 1;
1722 /* special case 2: in game engines before 3.1.1, blocking was different */
1723 if (game.use_block_last_field_bug)
1724 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1726 if (!options.network || player->connected)
1728 player->active = TRUE;
1730 /* remove potentially duplicate players */
1731 if (StorePlayer[jx][jy] == Feld[x][y])
1732 StorePlayer[jx][jy] = 0;
1734 StorePlayer[x][y] = Feld[x][y];
1738 printf("Player %d activated.\n", player->element_nr);
1739 printf("[Local player is %d and currently %s.]\n",
1740 local_player->element_nr,
1741 local_player->active ? "active" : "not active");
1745 Feld[x][y] = EL_EMPTY;
1747 player->jx = player->last_jx = x;
1748 player->jy = player->last_jy = y;
1752 static void InitField(int x, int y, boolean init_game)
1754 int element = Feld[x][y];
1763 InitPlayerField(x, y, element, init_game);
1766 case EL_SOKOBAN_FIELD_PLAYER:
1767 element = Feld[x][y] = EL_PLAYER_1;
1768 InitField(x, y, init_game);
1770 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1771 InitField(x, y, init_game);
1774 case EL_SOKOBAN_FIELD_EMPTY:
1775 local_player->sokobanfields_still_needed++;
1779 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1780 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1781 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1782 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1783 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1784 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1785 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1786 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1787 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1788 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1797 case EL_SPACESHIP_RIGHT:
1798 case EL_SPACESHIP_UP:
1799 case EL_SPACESHIP_LEFT:
1800 case EL_SPACESHIP_DOWN:
1801 case EL_BD_BUTTERFLY:
1802 case EL_BD_BUTTERFLY_RIGHT:
1803 case EL_BD_BUTTERFLY_UP:
1804 case EL_BD_BUTTERFLY_LEFT:
1805 case EL_BD_BUTTERFLY_DOWN:
1807 case EL_BD_FIREFLY_RIGHT:
1808 case EL_BD_FIREFLY_UP:
1809 case EL_BD_FIREFLY_LEFT:
1810 case EL_BD_FIREFLY_DOWN:
1811 case EL_PACMAN_RIGHT:
1813 case EL_PACMAN_LEFT:
1814 case EL_PACMAN_DOWN:
1816 case EL_YAMYAM_LEFT:
1817 case EL_YAMYAM_RIGHT:
1819 case EL_YAMYAM_DOWN:
1820 case EL_DARK_YAMYAM:
1823 case EL_SP_SNIKSNAK:
1824 case EL_SP_ELECTRON:
1833 case EL_AMOEBA_FULL:
1838 case EL_AMOEBA_DROP:
1839 if (y == lev_fieldy - 1)
1841 Feld[x][y] = EL_AMOEBA_GROWING;
1842 Store[x][y] = EL_AMOEBA_WET;
1846 case EL_DYNAMITE_ACTIVE:
1847 case EL_SP_DISK_RED_ACTIVE:
1848 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1849 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1850 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1851 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1852 MovDelay[x][y] = 96;
1855 case EL_EM_DYNAMITE_ACTIVE:
1856 MovDelay[x][y] = 32;
1860 local_player->lights_still_needed++;
1864 local_player->friends_still_needed++;
1869 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1872 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1873 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1874 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1875 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1876 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1877 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1878 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1879 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1880 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1881 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1882 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1883 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1886 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1887 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1888 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1890 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1892 game.belt_dir[belt_nr] = belt_dir;
1893 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1895 else /* more than one switch -- set it like the first switch */
1897 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1902 #if !USE_BOTH_SWITCHGATE_SWITCHES
1903 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1905 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1908 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1910 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1914 case EL_LIGHT_SWITCH_ACTIVE:
1916 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1919 case EL_INVISIBLE_STEELWALL:
1920 case EL_INVISIBLE_WALL:
1921 case EL_INVISIBLE_SAND:
1922 if (game.light_time_left > 0 ||
1923 game.lenses_time_left > 0)
1924 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1927 case EL_EMC_MAGIC_BALL:
1928 if (game.ball_state)
1929 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1932 case EL_EMC_MAGIC_BALL_SWITCH:
1933 if (game.ball_state)
1934 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1938 if (IS_CUSTOM_ELEMENT(element))
1940 if (CAN_MOVE(element))
1943 #if USE_NEW_CUSTOM_VALUE
1944 if (!element_info[element].use_last_ce_value || init_game)
1945 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1948 else if (IS_GROUP_ELEMENT(element))
1950 Feld[x][y] = GetElementFromGroupElement(element);
1952 InitField(x, y, init_game);
1959 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1962 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1964 InitField(x, y, init_game);
1966 /* not needed to call InitMovDir() -- already done by InitField()! */
1967 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1968 CAN_MOVE(Feld[x][y]))
1972 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1974 int old_element = Feld[x][y];
1976 InitField(x, y, init_game);
1978 /* not needed to call InitMovDir() -- already done by InitField()! */
1979 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1980 CAN_MOVE(old_element) &&
1981 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1984 /* this case is in fact a combination of not less than three bugs:
1985 first, it calls InitMovDir() for elements that can move, although this is
1986 already done by InitField(); then, it checks the element that was at this
1987 field _before_ the call to InitField() (which can change it); lastly, it
1988 was not called for "mole with direction" elements, which were treated as
1989 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1995 static int get_key_element_from_nr(int key_nr)
1997 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1998 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1999 EL_EM_KEY_1 : EL_KEY_1);
2001 return key_base_element + key_nr;
2004 static int get_next_dropped_element(struct PlayerInfo *player)
2006 return (player->inventory_size > 0 ?
2007 player->inventory_element[player->inventory_size - 1] :
2008 player->inventory_infinite_element != EL_UNDEFINED ?
2009 player->inventory_infinite_element :
2010 player->dynabombs_left > 0 ?
2011 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2015 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2017 /* pos >= 0: get element from bottom of the stack;
2018 pos < 0: get element from top of the stack */
2022 int min_inventory_size = -pos;
2023 int inventory_pos = player->inventory_size - min_inventory_size;
2024 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2026 return (player->inventory_size >= min_inventory_size ?
2027 player->inventory_element[inventory_pos] :
2028 player->inventory_infinite_element != EL_UNDEFINED ?
2029 player->inventory_infinite_element :
2030 player->dynabombs_left >= min_dynabombs_left ?
2031 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2036 int min_dynabombs_left = pos + 1;
2037 int min_inventory_size = pos + 1 - player->dynabombs_left;
2038 int inventory_pos = pos - player->dynabombs_left;
2040 return (player->inventory_infinite_element != EL_UNDEFINED ?
2041 player->inventory_infinite_element :
2042 player->dynabombs_left >= min_dynabombs_left ?
2043 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2044 player->inventory_size >= min_inventory_size ?
2045 player->inventory_element[inventory_pos] :
2050 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2052 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2053 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2056 if (gpo1->sort_priority != gpo2->sort_priority)
2057 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2059 compare_result = gpo1->nr - gpo2->nr;
2061 return compare_result;
2064 void InitGameControlValues()
2068 for (i = 0; game_panel_controls[i].nr != -1; i++)
2070 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2071 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2072 struct TextPosInfo *pos = gpc->pos;
2074 int type = gpc->type;
2078 Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2079 Error(ERR_EXIT, "this should not happen -- please debug");
2082 /* force update of game controls after initialization */
2083 gpc->value = gpc->last_value = -1;
2084 gpc->frame = gpc->last_frame = -1;
2085 gpc->gfx_frame = -1;
2087 /* determine panel value width for later calculation of alignment */
2088 if (type == TYPE_INTEGER || type == TYPE_STRING)
2090 pos->width = pos->size * getFontWidth(pos->font);
2091 pos->height = getFontHeight(pos->font);
2093 else if (type == TYPE_ELEMENT)
2095 pos->width = pos->size;
2096 pos->height = pos->size;
2099 /* fill structure for game panel draw order */
2101 gpo->sort_priority = pos->sort_priority;
2104 /* sort game panel controls according to sort_priority and control number */
2105 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2106 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2109 void UpdatePlayfieldElementCount()
2113 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2114 element_info[i].element_count = 0;
2116 SCAN_PLAYFIELD(x, y)
2118 element_info[Feld[x][y]].element_count++;
2121 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2122 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2123 if (IS_IN_GROUP(j, i))
2124 element_info[EL_GROUP_START + i].element_count +=
2125 element_info[j].element_count;
2128 void UpdateGameControlValues()
2131 int time = (local_player->LevelSolved ?
2132 local_player->LevelSolved_CountingTime :
2133 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134 level.native_em_level->lev->time :
2135 level.time == 0 ? TimePlayed : TimeLeft);
2136 int score = (local_player->LevelSolved ?
2137 local_player->LevelSolved_CountingScore :
2138 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2139 level.native_em_level->lev->score :
2140 local_player->score);
2141 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142 level.native_em_level->lev->required :
2143 local_player->gems_still_needed);
2144 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2145 level.native_em_level->lev->required > 0 :
2146 local_player->gems_still_needed > 0 ||
2147 local_player->sokobanfields_still_needed > 0 ||
2148 local_player->lights_still_needed > 0);
2150 UpdatePlayfieldElementCount();
2152 /* update game panel control values */
2154 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2155 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2157 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2158 for (i = 0; i < MAX_NUM_KEYS; i++)
2159 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2160 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2161 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2163 if (game.centered_player_nr == -1)
2165 for (i = 0; i < MAX_PLAYERS; i++)
2167 for (k = 0; k < MAX_NUM_KEYS; k++)
2169 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2171 if (level.native_em_level->ply[i]->keys & (1 << k))
2172 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2173 get_key_element_from_nr(k);
2175 else if (stored_player[i].key[k])
2176 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2177 get_key_element_from_nr(k);
2180 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2181 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2182 level.native_em_level->ply[i]->dynamite;
2184 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2185 stored_player[i].inventory_size;
2187 if (stored_player[i].num_white_keys > 0)
2188 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2191 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2192 stored_player[i].num_white_keys;
2197 int player_nr = game.centered_player_nr;
2199 for (k = 0; k < MAX_NUM_KEYS; k++)
2201 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2203 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2204 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2205 get_key_element_from_nr(k);
2207 else if (stored_player[player_nr].key[k])
2208 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2209 get_key_element_from_nr(k);
2212 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2214 level.native_em_level->ply[player_nr]->dynamite;
2216 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2217 stored_player[player_nr].inventory_size;
2219 if (stored_player[player_nr].num_white_keys > 0)
2220 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2222 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2223 stored_player[player_nr].num_white_keys;
2226 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2228 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2229 get_inventory_element_from_pos(local_player, i);
2230 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2231 get_inventory_element_from_pos(local_player, -i - 1);
2234 game_panel_controls[GAME_PANEL_SCORE].value = score;
2236 game_panel_controls[GAME_PANEL_TIME].value = time;
2238 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2239 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2240 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2242 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2243 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2245 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2246 local_player->shield_normal_time_left;
2247 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2248 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2250 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2251 local_player->shield_deadly_time_left;
2253 game_panel_controls[GAME_PANEL_EXIT].value =
2254 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2256 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2257 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2258 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2259 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2260 EL_EMC_MAGIC_BALL_SWITCH);
2262 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2263 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2264 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2265 game.light_time_left;
2267 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2268 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2269 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2270 game.timegate_time_left;
2272 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2273 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2275 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2276 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2277 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2278 game.lenses_time_left;
2280 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2281 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2282 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2283 game.magnify_time_left;
2285 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2286 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2287 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2288 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2289 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2290 EL_BALLOON_SWITCH_NONE);
2292 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2293 local_player->dynabomb_count;
2294 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2295 local_player->dynabomb_size;
2296 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2297 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2299 game_panel_controls[GAME_PANEL_PENGUINS].value =
2300 local_player->friends_still_needed;
2302 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2303 local_player->sokobanfields_still_needed;
2304 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2305 local_player->sokobanfields_still_needed;
2307 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2308 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2310 for (i = 0; i < NUM_BELTS; i++)
2312 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2313 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2314 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2315 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2316 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2319 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2320 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2321 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2322 game.magic_wall_time_left;
2324 #if USE_PLAYER_GRAVITY
2325 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2326 local_player->gravity;
2328 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2331 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2332 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2334 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2335 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2336 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2337 game.panel.element[i].id : EL_UNDEFINED);
2339 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2340 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2341 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2342 element_info[game.panel.element_count[i].id].element_count :
2345 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2346 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2347 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2348 element_info[game.panel.ce_score[i].id].collect_score : 0);
2350 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2351 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2352 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2353 element_info[game.panel.ce_score_element[i].id].collect_score :
2356 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2357 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2358 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2360 /* update game panel control frames */
2362 for (i = 0; game_panel_controls[i].nr != -1; i++)
2364 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2366 if (gpc->type == TYPE_ELEMENT)
2368 int last_anim_random_frame = gfx.anim_random_frame;
2369 int element = gpc->value;
2370 int graphic = el2panelimg(element);
2372 if (gpc->value != gpc->last_value)
2375 gpc->gfx_random = INIT_GFX_RANDOM();
2381 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2382 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2383 gpc->gfx_random = INIT_GFX_RANDOM();
2386 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2387 gfx.anim_random_frame = gpc->gfx_random;
2389 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2390 gpc->gfx_frame = element_info[element].collect_score;
2392 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2395 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2396 gfx.anim_random_frame = last_anim_random_frame;
2401 void DisplayGameControlValues()
2403 boolean redraw_panel = FALSE;
2406 for (i = 0; game_panel_controls[i].nr != -1; i++)
2408 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2410 if (PANEL_DEACTIVATED(gpc->pos))
2413 if (gpc->value == gpc->last_value &&
2414 gpc->frame == gpc->last_frame)
2417 redraw_panel = TRUE;
2423 /* copy default game door content to main double buffer */
2424 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2425 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2427 /* redraw game control buttons */
2429 RedrawGameButtons();
2435 game_status = GAME_MODE_PSEUDO_PANEL;
2438 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2440 for (i = 0; game_panel_controls[i].nr != -1; i++)
2444 int nr = game_panel_order[i].nr;
2445 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2447 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2450 struct TextPosInfo *pos = gpc->pos;
2451 int type = gpc->type;
2452 int value = gpc->value;
2453 int frame = gpc->frame;
2455 int last_value = gpc->last_value;
2456 int last_frame = gpc->last_frame;
2458 int size = pos->size;
2459 int font = pos->font;
2460 boolean draw_masked = pos->draw_masked;
2461 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463 if (PANEL_DEACTIVATED(pos))
2467 if (value == last_value && frame == last_frame)
2471 gpc->last_value = value;
2472 gpc->last_frame = frame;
2475 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2478 if (type == TYPE_INTEGER)
2480 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2481 nr == GAME_PANEL_TIME)
2483 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2485 if (use_dynamic_size) /* use dynamic number of digits */
2487 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2488 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2489 int size2 = size1 + 1;
2490 int font1 = pos->font;
2491 int font2 = pos->font_alt;
2493 size = (value < value_change ? size1 : size2);
2494 font = (value < value_change ? font1 : font2);
2497 /* clear background if value just changed its size (dynamic digits) */
2498 if ((last_value < value_change) != (value < value_change))
2500 int width1 = size1 * getFontWidth(font1);
2501 int width2 = size2 * getFontWidth(font2);
2502 int max_width = MAX(width1, width2);
2503 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2505 pos->width = max_width;
2507 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2508 max_width, max_height);
2515 /* correct text size if "digits" is zero or less */
2517 size = strlen(int2str(value, size));
2519 /* dynamically correct text alignment */
2520 pos->width = size * getFontWidth(font);
2523 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2524 int2str(value, size), font, mask_mode);
2526 else if (type == TYPE_ELEMENT)
2528 int element, graphic;
2532 int dst_x = PANEL_XPOS(pos);
2533 int dst_y = PANEL_YPOS(pos);
2536 if (value != EL_UNDEFINED && value != EL_EMPTY)
2539 graphic = el2panelimg(value);
2541 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2544 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2548 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2551 width = graphic_info[graphic].width * size / TILESIZE;
2552 height = graphic_info[graphic].height * size / TILESIZE;
2556 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2557 dst_x - src_x, dst_y - src_y);
2558 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2563 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2568 if (value == EL_UNDEFINED || value == EL_EMPTY)
2570 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2571 graphic = el2panelimg(element);
2573 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2574 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2575 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2580 graphic = el2panelimg(value);
2582 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2585 width = graphic_info[graphic].width * size / TILESIZE;
2586 height = graphic_info[graphic].height * size / TILESIZE;
2588 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2591 else if (type == TYPE_STRING)
2593 boolean active = (value != 0);
2594 char *state_normal = "off";
2595 char *state_active = "on";
2596 char *state = (active ? state_active : state_normal);
2597 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2598 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2599 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2600 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2602 if (nr == GAME_PANEL_GRAVITY_STATE)
2604 int font1 = pos->font; /* (used for normal state) */
2605 int font2 = pos->font_alt; /* (used for active state) */
2607 int size1 = strlen(state_normal);
2608 int size2 = strlen(state_active);
2609 int width1 = size1 * getFontWidth(font1);
2610 int width2 = size2 * getFontWidth(font2);
2611 int max_width = MAX(width1, width2);
2612 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2614 pos->width = max_width;
2616 /* clear background for values that may have changed its size */
2617 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2618 max_width, max_height);
2621 font = (active ? font2 : font1);
2631 /* don't truncate output if "chars" is zero or less */
2634 /* dynamically correct text alignment */
2635 pos->width = size * getFontWidth(font);
2639 s_cut = getStringCopyN(s, size);
2641 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642 s_cut, font, mask_mode);
2648 redraw_mask |= REDRAW_DOOR_1;
2651 game_status = GAME_MODE_PLAYING;
2654 void DrawGameValue_Emeralds(int value)
2656 struct TextPosInfo *pos = &game.panel.gems;
2658 int font_nr = pos->font;
2660 int font_nr = FONT_TEXT_2;
2662 int font_width = getFontWidth(font_nr);
2663 int chars = pos->size;
2666 return; /* !!! USE NEW STUFF !!! */
2669 if (PANEL_DEACTIVATED(pos))
2672 pos->width = chars * font_width;
2674 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2677 void DrawGameValue_Dynamite(int value)
2679 struct TextPosInfo *pos = &game.panel.inventory_count;
2681 int font_nr = pos->font;
2683 int font_nr = FONT_TEXT_2;
2685 int font_width = getFontWidth(font_nr);
2686 int chars = pos->size;
2689 return; /* !!! USE NEW STUFF !!! */
2692 if (PANEL_DEACTIVATED(pos))
2695 pos->width = chars * font_width;
2697 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2700 void DrawGameValue_Score(int value)
2702 struct TextPosInfo *pos = &game.panel.score;
2704 int font_nr = pos->font;
2706 int font_nr = FONT_TEXT_2;
2708 int font_width = getFontWidth(font_nr);
2709 int chars = pos->size;
2712 return; /* !!! USE NEW STUFF !!! */
2715 if (PANEL_DEACTIVATED(pos))
2718 pos->width = chars * font_width;
2720 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2723 void DrawGameValue_Time(int value)
2725 struct TextPosInfo *pos = &game.panel.time;
2726 static int last_value = -1;
2729 int chars = pos->size;
2731 int font1_nr = pos->font;
2732 int font2_nr = pos->font_alt;
2734 int font1_nr = FONT_TEXT_2;
2735 int font2_nr = FONT_TEXT_1;
2737 int font_nr = font1_nr;
2738 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2741 return; /* !!! USE NEW STUFF !!! */
2744 if (PANEL_DEACTIVATED(pos))
2747 if (use_dynamic_chars) /* use dynamic number of chars */
2749 chars = (value < 1000 ? chars1 : chars2);
2750 font_nr = (value < 1000 ? font1_nr : font2_nr);
2753 /* clear background if value just changed its size (dynamic chars only) */
2754 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2756 int width1 = chars1 * getFontWidth(font1_nr);
2757 int width2 = chars2 * getFontWidth(font2_nr);
2758 int max_width = MAX(width1, width2);
2759 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2761 pos->width = max_width;
2763 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2764 max_width, max_height);
2767 pos->width = chars * getFontWidth(font_nr);
2769 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2774 void DrawGameValue_Level(int value)
2776 struct TextPosInfo *pos = &game.panel.level_number;
2779 int chars = pos->size;
2781 int font1_nr = pos->font;
2782 int font2_nr = pos->font_alt;
2784 int font1_nr = FONT_TEXT_2;
2785 int font2_nr = FONT_TEXT_1;
2787 int font_nr = font1_nr;
2788 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2791 return; /* !!! USE NEW STUFF !!! */
2794 if (PANEL_DEACTIVATED(pos))
2797 if (use_dynamic_chars) /* use dynamic number of chars */
2799 chars = (level_nr < 100 ? chars1 : chars2);
2800 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2803 pos->width = chars * getFontWidth(font_nr);
2805 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2808 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2811 struct TextPosInfo *pos = &game.panel.keys;
2814 int base_key_graphic = EL_KEY_1;
2819 return; /* !!! USE NEW STUFF !!! */
2823 if (PANEL_DEACTIVATED(pos))
2828 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2829 base_key_graphic = EL_EM_KEY_1;
2833 pos->width = 4 * MINI_TILEX;
2837 for (i = 0; i < MAX_NUM_KEYS; i++)
2839 /* currently only 4 of 8 possible keys are displayed */
2840 for (i = 0; i < STD_NUM_KEYS; i++)
2844 struct TextPosInfo *pos = &game.panel.key[i];
2846 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2847 int src_y = DOOR_GFX_PAGEY1 + 123;
2849 int dst_x = PANEL_XPOS(pos);
2850 int dst_y = PANEL_YPOS(pos);
2852 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2853 int dst_y = PANEL_YPOS(pos);
2857 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2858 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2860 int graphic = el2edimg(element);
2864 if (PANEL_DEACTIVATED(pos))
2869 /* masked blit with tiles from half-size scaled bitmap does not work yet
2870 (no mask bitmap created for these sizes after loading and scaling) --
2871 solution: load without creating mask, scale, then create final mask */
2873 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2874 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2879 int graphic = el2edimg(base_key_graphic + i);
2884 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2886 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2887 dst_x - src_x, dst_y - src_y);
2888 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2894 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2896 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2897 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2900 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2902 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2903 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2911 void DrawGameValue_Emeralds(int value)
2913 int font_nr = FONT_TEXT_2;
2914 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2916 if (PANEL_DEACTIVATED(game.panel.gems))
2919 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2922 void DrawGameValue_Dynamite(int value)
2924 int font_nr = FONT_TEXT_2;
2925 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2927 if (PANEL_DEACTIVATED(game.panel.inventory_count))
2930 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2933 void DrawGameValue_Score(int value)
2935 int font_nr = FONT_TEXT_2;
2936 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2938 if (PANEL_DEACTIVATED(game.panel.score))
2941 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2944 void DrawGameValue_Time(int value)
2946 int font1_nr = FONT_TEXT_2;
2948 int font2_nr = FONT_TEXT_1;
2950 int font2_nr = FONT_LEVEL_NUMBER;
2952 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2953 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2955 if (PANEL_DEACTIVATED(game.panel.time))
2958 /* clear background if value just changed its size */
2959 if (value == 999 || value == 1000)
2960 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2963 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2965 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2968 void DrawGameValue_Level(int value)
2970 int font1_nr = FONT_TEXT_2;
2972 int font2_nr = FONT_TEXT_1;
2974 int font2_nr = FONT_LEVEL_NUMBER;
2977 if (PANEL_DEACTIVATED(game.panel.level))
2981 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2983 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2986 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2988 int base_key_graphic = EL_KEY_1;
2991 if (PANEL_DEACTIVATED(game.panel.keys))
2994 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2995 base_key_graphic = EL_EM_KEY_1;
2997 /* currently only 4 of 8 possible keys are displayed */
2998 for (i = 0; i < STD_NUM_KEYS; i++)
3000 int x = XX_KEYS + i * MINI_TILEX;
3004 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3006 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3007 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3013 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3016 int key[MAX_NUM_KEYS];
3019 /* prevent EM engine from updating time/score values parallel to GameWon() */
3020 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3021 local_player->LevelSolved)
3024 for (i = 0; i < MAX_NUM_KEYS; i++)
3025 key[i] = key_bits & (1 << i);
3027 DrawGameValue_Level(level_nr);
3029 DrawGameValue_Emeralds(emeralds);
3030 DrawGameValue_Dynamite(dynamite);
3031 DrawGameValue_Score(score);
3032 DrawGameValue_Time(time);
3034 DrawGameValue_Keys(key);
3037 void UpdateGameDoorValues()
3039 UpdateGameControlValues();
3042 void DrawGameDoorValues()
3044 DisplayGameControlValues();
3047 void DrawGameDoorValues_OLD()
3049 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3050 int dynamite_value = 0;
3051 int score_value = (local_player->LevelSolved ? local_player->score_final :
3052 local_player->score);
3053 int gems_value = local_player->gems_still_needed;
3057 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3059 DrawGameDoorValues_EM();
3064 if (game.centered_player_nr == -1)
3066 for (i = 0; i < MAX_PLAYERS; i++)
3068 for (j = 0; j < MAX_NUM_KEYS; j++)
3069 if (stored_player[i].key[j])
3070 key_bits |= (1 << j);
3072 dynamite_value += stored_player[i].inventory_size;
3077 int player_nr = game.centered_player_nr;
3079 for (i = 0; i < MAX_NUM_KEYS; i++)
3080 if (stored_player[player_nr].key[i])
3081 key_bits |= (1 << i);
3083 dynamite_value = stored_player[player_nr].inventory_size;
3086 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3092 =============================================================================
3094 -----------------------------------------------------------------------------
3095 initialize game engine due to level / tape version number
3096 =============================================================================
3099 static void InitGameEngine()
3101 int i, j, k, l, x, y;
3103 /* set game engine from tape file when re-playing, else from level file */
3104 game.engine_version = (tape.playing ? tape.engine_version :
3105 level.game_version);
3107 /* ---------------------------------------------------------------------- */
3108 /* set flags for bugs and changes according to active game engine version */
3109 /* ---------------------------------------------------------------------- */
3112 Summary of bugfix/change:
3113 Fixed handling for custom elements that change when pushed by the player.
3115 Fixed/changed in version:
3119 Before 3.1.0, custom elements that "change when pushing" changed directly
3120 after the player started pushing them (until then handled in "DigField()").
3121 Since 3.1.0, these custom elements are not changed until the "pushing"
3122 move of the element is finished (now handled in "ContinueMoving()").
3124 Affected levels/tapes:
3125 The first condition is generally needed for all levels/tapes before version
3126 3.1.0, which might use the old behaviour before it was changed; known tapes
3127 that are affected are some tapes from the level set "Walpurgis Gardens" by
3129 The second condition is an exception from the above case and is needed for
3130 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3131 above (including some development versions of 3.1.0), but before it was
3132 known that this change would break tapes like the above and was fixed in
3133 3.1.1, so that the changed behaviour was active although the engine version
3134 while recording maybe was before 3.1.0. There is at least one tape that is
3135 affected by this exception, which is the tape for the one-level set "Bug
3136 Machine" by Juergen Bonhagen.
3139 game.use_change_when_pushing_bug =
3140 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3142 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3143 tape.game_version < VERSION_IDENT(3,1,1,0)));
3146 Summary of bugfix/change:
3147 Fixed handling for blocking the field the player leaves when moving.
3149 Fixed/changed in version:
3153 Before 3.1.1, when "block last field when moving" was enabled, the field
3154 the player is leaving when moving was blocked for the time of the move,
3155 and was directly unblocked afterwards. This resulted in the last field
3156 being blocked for exactly one less than the number of frames of one player
3157 move. Additionally, even when blocking was disabled, the last field was
3158 blocked for exactly one frame.
3159 Since 3.1.1, due to changes in player movement handling, the last field
3160 is not blocked at all when blocking is disabled. When blocking is enabled,
3161 the last field is blocked for exactly the number of frames of one player
3162 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3163 last field is blocked for exactly one more than the number of frames of
3166 Affected levels/tapes:
3167 (!!! yet to be determined -- probably many !!!)
3170 game.use_block_last_field_bug =
3171 (game.engine_version < VERSION_IDENT(3,1,1,0));
3174 Summary of bugfix/change:
3175 Changed behaviour of CE changes with multiple changes per single frame.
3177 Fixed/changed in version:
3181 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3182 This resulted in race conditions where CEs seem to behave strange in some
3183 situations (where triggered CE changes were just skipped because there was
3184 already a CE change on that tile in the playfield in that engine frame).
3185 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3186 (The number of changes per frame must be limited in any case, because else
3187 it is easily possible to define CE changes that would result in an infinite
3188 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3189 should be set large enough so that it would only be reached in cases where
3190 the corresponding CE change conditions run into a loop. Therefore, it seems
3191 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3192 maximal number of change pages for custom elements.)
3194 Affected levels/tapes:
3198 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3199 game.max_num_changes_per_frame = 1;
3201 game.max_num_changes_per_frame =
3202 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3205 /* ---------------------------------------------------------------------- */
3207 /* default scan direction: scan playfield from top/left to bottom/right */
3208 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3210 /* dynamically adjust element properties according to game engine version */
3211 InitElementPropertiesEngine(game.engine_version);
3214 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3215 printf(" tape version == %06d [%s] [file: %06d]\n",
3216 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3218 printf(" => game.engine_version == %06d\n", game.engine_version);
3221 /* ---------- initialize player's initial move delay --------------------- */
3223 /* dynamically adjust player properties according to level information */
3224 for (i = 0; i < MAX_PLAYERS; i++)
3225 game.initial_move_delay_value[i] =
3226 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3228 /* dynamically adjust player properties according to game engine version */
3229 for (i = 0; i < MAX_PLAYERS; i++)
3230 game.initial_move_delay[i] =
3231 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3232 game.initial_move_delay_value[i] : 0);
3234 /* ---------- initialize player's initial push delay --------------------- */
3236 /* dynamically adjust player properties according to game engine version */
3237 game.initial_push_delay_value =
3238 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3240 /* ---------- initialize changing elements ------------------------------- */
3242 /* initialize changing elements information */
3243 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3245 struct ElementInfo *ei = &element_info[i];
3247 /* this pointer might have been changed in the level editor */
3248 ei->change = &ei->change_page[0];
3250 if (!IS_CUSTOM_ELEMENT(i))
3252 ei->change->target_element = EL_EMPTY_SPACE;
3253 ei->change->delay_fixed = 0;
3254 ei->change->delay_random = 0;
3255 ei->change->delay_frames = 1;
3258 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3260 ei->has_change_event[j] = FALSE;
3262 ei->event_page_nr[j] = 0;
3263 ei->event_page[j] = &ei->change_page[0];
3267 /* add changing elements from pre-defined list */
3268 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3270 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3271 struct ElementInfo *ei = &element_info[ch_delay->element];
3273 ei->change->target_element = ch_delay->target_element;
3274 ei->change->delay_fixed = ch_delay->change_delay;
3276 ei->change->pre_change_function = ch_delay->pre_change_function;
3277 ei->change->change_function = ch_delay->change_function;
3278 ei->change->post_change_function = ch_delay->post_change_function;
3280 ei->change->can_change = TRUE;
3281 ei->change->can_change_or_has_action = TRUE;
3283 ei->has_change_event[CE_DELAY] = TRUE;
3285 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3286 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3289 /* ---------- initialize internal run-time variables ------------- */
3291 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3293 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3295 for (j = 0; j < ei->num_change_pages; j++)
3297 ei->change_page[j].can_change_or_has_action =
3298 (ei->change_page[j].can_change |
3299 ei->change_page[j].has_action);
3303 /* add change events from custom element configuration */
3304 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3306 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3308 for (j = 0; j < ei->num_change_pages; j++)
3310 if (!ei->change_page[j].can_change_or_has_action)
3313 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3315 /* only add event page for the first page found with this event */
3316 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3318 ei->has_change_event[k] = TRUE;
3320 ei->event_page_nr[k] = j;
3321 ei->event_page[k] = &ei->change_page[j];
3327 /* ---------- initialize run-time trigger player and element ------------- */
3329 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3331 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3333 for (j = 0; j < ei->num_change_pages; j++)
3335 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3336 ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3337 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3338 ei->change_page[j].actual_trigger_ce_value = 0;
3339 ei->change_page[j].actual_trigger_ce_score = 0;
3343 /* ---------- initialize trigger events ---------------------------------- */
3345 /* initialize trigger events information */
3346 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3347 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3348 trigger_events[i][j] = FALSE;
3350 /* add trigger events from element change event properties */
3351 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3353 struct ElementInfo *ei = &element_info[i];
3355 for (j = 0; j < ei->num_change_pages; j++)
3357 if (!ei->change_page[j].can_change_or_has_action)
3360 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3362 int trigger_element = ei->change_page[j].trigger_element;
3364 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3366 if (ei->change_page[j].has_event[k])
3368 if (IS_GROUP_ELEMENT(trigger_element))
3370 struct ElementGroupInfo *group =
3371 element_info[trigger_element].group;
3373 for (l = 0; l < group->num_elements_resolved; l++)
3374 trigger_events[group->element_resolved[l]][k] = TRUE;
3376 else if (trigger_element == EL_ANY_ELEMENT)
3377 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3378 trigger_events[l][k] = TRUE;
3380 trigger_events[trigger_element][k] = TRUE;
3387 /* ---------- initialize push delay -------------------------------------- */
3389 /* initialize push delay values to default */
3390 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3392 if (!IS_CUSTOM_ELEMENT(i))
3394 /* set default push delay values (corrected since version 3.0.7-1) */
3395 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3397 element_info[i].push_delay_fixed = 2;
3398 element_info[i].push_delay_random = 8;
3402 element_info[i].push_delay_fixed = 8;
3403 element_info[i].push_delay_random = 8;
3408 /* set push delay value for certain elements from pre-defined list */
3409 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3411 int e = push_delay_list[i].element;
3413 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3414 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3417 /* set push delay value for Supaplex elements for newer engine versions */
3418 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3420 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422 if (IS_SP_ELEMENT(i))
3424 /* set SP push delay to just enough to push under a falling zonk */
3425 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3427 element_info[i].push_delay_fixed = delay;
3428 element_info[i].push_delay_random = 0;
3433 /* ---------- initialize move stepsize ----------------------------------- */
3435 /* initialize move stepsize values to default */
3436 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3437 if (!IS_CUSTOM_ELEMENT(i))
3438 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3440 /* set move stepsize value for certain elements from pre-defined list */
3441 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3443 int e = move_stepsize_list[i].element;
3445 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3448 /* ---------- initialize collect score ----------------------------------- */
3450 /* initialize collect score values for custom elements from initial value */
3451 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3452 if (IS_CUSTOM_ELEMENT(i))
3453 element_info[i].collect_score = element_info[i].collect_score_initial;
3455 /* ---------- initialize collect count ----------------------------------- */
3457 /* initialize collect count values for non-custom elements */
3458 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3459 if (!IS_CUSTOM_ELEMENT(i))
3460 element_info[i].collect_count_initial = 0;
3462 /* add collect count values for all elements from pre-defined list */
3463 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3464 element_info[collect_count_list[i].element].collect_count_initial =
3465 collect_count_list[i].count;
3467 /* ---------- initialize access direction -------------------------------- */
3469 /* initialize access direction values to default (access from every side) */
3470 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3471 if (!IS_CUSTOM_ELEMENT(i))
3472 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3474 /* set access direction value for certain elements from pre-defined list */
3475 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3476 element_info[access_direction_list[i].element].access_direction =
3477 access_direction_list[i].direction;
3479 /* ---------- initialize explosion content ------------------------------- */
3480 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3482 if (IS_CUSTOM_ELEMENT(i))
3485 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3487 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3489 element_info[i].content.e[x][y] =
3490 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3491 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3492 i == EL_PLAYER_3 ? EL_EMERALD :
3493 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3494 i == EL_MOLE ? EL_EMERALD_RED :
3495 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3496 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3497 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3498 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3499 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3500 i == EL_WALL_EMERALD ? EL_EMERALD :
3501 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3502 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3503 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3504 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3505 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3506 i == EL_WALL_PEARL ? EL_PEARL :
3507 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3512 /* ---------- initialize recursion detection ------------------------------ */
3513 recursion_loop_depth = 0;
3514 recursion_loop_detected = FALSE;
3515 recursion_loop_element = EL_UNDEFINED;
3517 /* ---------- initialize graphics engine ---------------------------------- */
3518 game.scroll_delay_value =
3519 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3520 setup.scroll_delay ? setup.scroll_delay_value : 0);
3521 game.scroll_delay_value =
3522 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3525 int get_num_special_action(int element, int action_first, int action_last)
3527 int num_special_action = 0;
3530 for (i = action_first; i <= action_last; i++)
3532 boolean found = FALSE;
3534 for (j = 0; j < NUM_DIRECTIONS; j++)
3535 if (el_act_dir2img(element, i, j) !=
3536 el_act_dir2img(element, ACTION_DEFAULT, j))
3540 num_special_action++;
3545 return num_special_action;
3550 =============================================================================
3552 -----------------------------------------------------------------------------
3553 initialize and start new game
3554 =============================================================================
3559 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3560 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3561 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3563 boolean do_fading = (game_status == GAME_MODE_MAIN);
3567 game_status = GAME_MODE_PLAYING;
3570 InitGameControlValues();
3572 /* don't play tapes over network */
3573 network_playing = (options.network && !tape.playing);
3575 for (i = 0; i < MAX_PLAYERS; i++)
3577 struct PlayerInfo *player = &stored_player[i];
3579 player->index_nr = i;
3580 player->index_bit = (1 << i);
3581 player->element_nr = EL_PLAYER_1 + i;
3583 player->present = FALSE;
3584 player->active = FALSE;
3585 player->killed = FALSE;
3588 player->effective_action = 0;
3589 player->programmed_action = 0;
3592 player->score_final = 0;
3594 player->gems_still_needed = level.gems_needed;
3595 player->sokobanfields_still_needed = 0;
3596 player->lights_still_needed = 0;
3597 player->friends_still_needed = 0;
3599 for (j = 0; j < MAX_NUM_KEYS; j++)
3600 player->key[j] = FALSE;
3602 player->num_white_keys = 0;
3604 player->dynabomb_count = 0;
3605 player->dynabomb_size = 1;
3606 player->dynabombs_left = 0;
3607 player->dynabomb_xl = FALSE;
3609 player->MovDir = MV_NONE;
3612 player->GfxDir = MV_NONE;
3613 player->GfxAction = ACTION_DEFAULT;
3615 player->StepFrame = 0;
3617 player->use_murphy = FALSE;
3618 player->artwork_element =
3619 (level.use_artwork_element[i] ? level.artwork_element[i] :
3620 player->element_nr);
3622 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3623 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3625 player->gravity = level.initial_player_gravity[i];
3627 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3629 player->actual_frame_counter = 0;
3631 player->step_counter = 0;
3633 player->last_move_dir = MV_NONE;
3635 player->is_active = FALSE;
3637 player->is_waiting = FALSE;
3638 player->is_moving = FALSE;
3639 player->is_auto_moving = FALSE;
3640 player->is_digging = FALSE;
3641 player->is_snapping = FALSE;
3642 player->is_collecting = FALSE;
3643 player->is_pushing = FALSE;
3644 player->is_switching = FALSE;
3645 player->is_dropping = FALSE;
3646 player->is_dropping_pressed = FALSE;
3648 player->is_bored = FALSE;
3649 player->is_sleeping = FALSE;
3651 player->frame_counter_bored = -1;
3652 player->frame_counter_sleeping = -1;
3654 player->anim_delay_counter = 0;
3655 player->post_delay_counter = 0;
3657 player->dir_waiting = MV_NONE;
3658 player->action_waiting = ACTION_DEFAULT;
3659 player->last_action_waiting = ACTION_DEFAULT;
3660 player->special_action_bored = ACTION_DEFAULT;
3661 player->special_action_sleeping = ACTION_DEFAULT;
3663 player->switch_x = -1;
3664 player->switch_y = -1;
3666 player->drop_x = -1;
3667 player->drop_y = -1;
3669 player->show_envelope = 0;
3671 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3673 player->push_delay = -1; /* initialized when pushing starts */
3674 player->push_delay_value = game.initial_push_delay_value;
3676 player->drop_delay = 0;
3677 player->drop_pressed_delay = 0;
3679 player->last_jx = -1;
3680 player->last_jy = -1;
3684 player->shield_normal_time_left = 0;
3685 player->shield_deadly_time_left = 0;
3687 player->inventory_infinite_element = EL_UNDEFINED;
3688 player->inventory_size = 0;
3690 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3691 SnapField(player, 0, 0);
3693 player->LevelSolved = FALSE;
3694 player->GameOver = FALSE;
3696 player->LevelSolved_GameWon = FALSE;
3697 player->LevelSolved_GameEnd = FALSE;
3698 player->LevelSolved_PanelOff = FALSE;
3699 player->LevelSolved_SaveTape = FALSE;
3700 player->LevelSolved_SaveScore = FALSE;
3701 player->LevelSolved_CountingTime = 0;
3702 player->LevelSolved_CountingScore = 0;
3705 network_player_action_received = FALSE;
3707 #if defined(NETWORK_AVALIABLE)
3708 /* initial null action */
3709 if (network_playing)
3710 SendToServer_MovePlayer(MV_NONE);
3719 TimeLeft = level.time;
3722 ScreenMovDir = MV_NONE;
3726 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3728 AllPlayersGone = FALSE;
3730 game.yamyam_content_nr = 0;
3731 game.robot_wheel_active = FALSE;
3732 game.magic_wall_active = FALSE;
3733 game.magic_wall_time_left = 0;
3734 game.light_time_left = 0;
3735 game.timegate_time_left = 0;
3736 game.switchgate_pos = 0;
3737 game.wind_direction = level.wind_direction_initial;
3739 #if !USE_PLAYER_GRAVITY
3740 game.gravity = FALSE;
3741 game.explosions_delayed = TRUE;
3744 game.lenses_time_left = 0;
3745 game.magnify_time_left = 0;
3747 game.ball_state = level.ball_state_initial;
3748 game.ball_content_nr = 0;
3750 game.envelope_active = FALSE;
3752 /* set focus to local player for network games, else to all players */
3753 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3754 game.centered_player_nr_next = game.centered_player_nr;
3755 game.set_centered_player = FALSE;
3757 if (network_playing && tape.recording)
3759 /* store client dependent player focus when recording network games */
3760 tape.centered_player_nr_next = game.centered_player_nr_next;
3761 tape.set_centered_player = TRUE;
3764 for (i = 0; i < NUM_BELTS; i++)
3766 game.belt_dir[i] = MV_NONE;
3767 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3770 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3771 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3773 SCAN_PLAYFIELD(x, y)
3775 Feld[x][y] = level.field[x][y];
3776 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3777 ChangeDelay[x][y] = 0;
3778 ChangePage[x][y] = -1;
3779 #if USE_NEW_CUSTOM_VALUE
3780 CustomValue[x][y] = 0; /* initialized in InitField() */
3782 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3784 WasJustMoving[x][y] = 0;
3785 WasJustFalling[x][y] = 0;
3786 CheckCollision[x][y] = 0;
3787 CheckImpact[x][y] = 0;
3789 Pushed[x][y] = FALSE;
3791 ChangeCount[x][y] = 0;
3792 ChangeEvent[x][y] = -1;
3794 ExplodePhase[x][y] = 0;
3795 ExplodeDelay[x][y] = 0;
3796 ExplodeField[x][y] = EX_TYPE_NONE;
3798 RunnerVisit[x][y] = 0;
3799 PlayerVisit[x][y] = 0;
3802 GfxRandom[x][y] = INIT_GFX_RANDOM();
3803 GfxElement[x][y] = EL_UNDEFINED;
3804 GfxAction[x][y] = ACTION_DEFAULT;
3805 GfxDir[x][y] = MV_NONE;
3808 SCAN_PLAYFIELD(x, y)
3810 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3812 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3814 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3817 InitField(x, y, TRUE);
3819 ResetGfxAnimation(x, y);
3824 for (i = 0; i < MAX_PLAYERS; i++)
3826 struct PlayerInfo *player = &stored_player[i];
3828 /* set number of special actions for bored and sleeping animation */
3829 player->num_special_action_bored =
3830 get_num_special_action(player->artwork_element,
3831 ACTION_BORING_1, ACTION_BORING_LAST);
3832 player->num_special_action_sleeping =
3833 get_num_special_action(player->artwork_element,
3834 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3837 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3838 emulate_sb ? EMU_SOKOBAN :
3839 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3841 #if USE_NEW_ALL_SLIPPERY
3842 /* initialize type of slippery elements */
3843 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3845 if (!IS_CUSTOM_ELEMENT(i))
3847 /* default: elements slip down either to the left or right randomly */
3848 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3850 /* SP style elements prefer to slip down on the left side */
3851 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3852 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3854 /* BD style elements prefer to slip down on the left side */
3855 if (game.emulation == EMU_BOULDERDASH)
3856 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3861 /* initialize explosion and ignition delay */
3862 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3864 if (!IS_CUSTOM_ELEMENT(i))
3867 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3868 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3869 game.emulation == EMU_SUPAPLEX ? 3 : 2);
3870 int last_phase = (num_phase + 1) * delay;
3871 int half_phase = (num_phase / 2) * delay;
3873 element_info[i].explosion_delay = last_phase - 1;
3874 element_info[i].ignition_delay = half_phase;
3876 if (i == EL_BLACK_ORB)
3877 element_info[i].ignition_delay = 1;
3881 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
3882 element_info[i].explosion_delay = 1;
3884 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
3885 element_info[i].ignition_delay = 1;
3889 /* correct non-moving belts to start moving left */
3890 for (i = 0; i < NUM_BELTS; i++)
3891 if (game.belt_dir[i] == MV_NONE)
3892 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3894 /* check if any connected player was not found in playfield */
3895 for (i = 0; i < MAX_PLAYERS; i++)
3897 struct PlayerInfo *player = &stored_player[i];
3899 if (player->connected && !player->present)
3901 for (j = 0; j < MAX_PLAYERS; j++)
3903 struct PlayerInfo *some_player = &stored_player[j];
3904 int jx = some_player->jx, jy = some_player->jy;
3906 /* assign first free player found that is present in the playfield */
3907 if (some_player->present && !some_player->connected)
3909 player->present = TRUE;
3910 player->active = TRUE;
3912 some_player->present = FALSE;
3913 some_player->active = FALSE;
3915 player->artwork_element = some_player->artwork_element;
3917 player->block_last_field = some_player->block_last_field;
3918 player->block_delay_adjustment = some_player->block_delay_adjustment;
3920 StorePlayer[jx][jy] = player->element_nr;
3921 player->jx = player->last_jx = jx;
3922 player->jy = player->last_jy = jy;
3932 /* when playing a tape, eliminate all players who do not participate */
3934 for (i = 0; i < MAX_PLAYERS; i++)
3936 if (stored_player[i].active && !tape.player_participates[i])
3938 struct PlayerInfo *player = &stored_player[i];
3939 int jx = player->jx, jy = player->jy;
3941 player->active = FALSE;
3942 StorePlayer[jx][jy] = 0;
3943 Feld[jx][jy] = EL_EMPTY;
3947 else if (!options.network && !setup.team_mode) /* && !tape.playing */
3949 /* when in single player mode, eliminate all but the first active player */
3951 for (i = 0; i < MAX_PLAYERS; i++)
3953 if (stored_player[i].active)
3955 for (j = i + 1; j < MAX_PLAYERS; j++)
3957 if (stored_player[j].active)
3959 struct PlayerInfo *player = &stored_player[j];
3960 int jx = player->jx, jy = player->jy;
3962 player->active = FALSE;
3963 player->present = FALSE;
3965 StorePlayer[jx][jy] = 0;
3966 Feld[jx][jy] = EL_EMPTY;
3973 /* when recording the game, store which players take part in the game */
3976 for (i = 0; i < MAX_PLAYERS; i++)
3977 if (stored_player[i].active)
3978 tape.player_participates[i] = TRUE;
3983 for (i = 0; i < MAX_PLAYERS; i++)
3985 struct PlayerInfo *player = &stored_player[i];
3987 printf("Player %d: present == %d, connected == %d, active == %d.\n",
3992 if (local_player == player)
3993 printf("Player %d is local player.\n", i+1);
3997 if (BorderElement == EL_EMPTY)
4000 SBX_Right = lev_fieldx - SCR_FIELDX;
4002 SBY_Lower = lev_fieldy - SCR_FIELDY;
4007 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4009 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4012 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4013 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4015 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4016 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4018 /* if local player not found, look for custom element that might create
4019 the player (make some assumptions about the right custom element) */
4020 if (!local_player->present)
4022 int start_x = 0, start_y = 0;
4023 int found_rating = 0;
4024 int found_element = EL_UNDEFINED;
4025 int player_nr = local_player->index_nr;
4027 SCAN_PLAYFIELD(x, y)
4029 int element = Feld[x][y];
4034 if (level.use_start_element[player_nr] &&
4035 level.start_element[player_nr] == element &&
4042 found_element = element;
4045 if (!IS_CUSTOM_ELEMENT(element))
4048 if (CAN_CHANGE(element))
4050 for (i = 0; i < element_info[element].num_change_pages; i++)
4052 /* check for player created from custom element as single target */
4053 content = element_info[element].change_page[i].target_element;
4054 is_player = ELEM_IS_PLAYER(content);
4056 if (is_player && (found_rating < 3 ||
4057 (found_rating == 3 && element < found_element)))
4063 found_element = element;
4068 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4070 /* check for player created from custom element as explosion content */
4071 content = element_info[element].content.e[xx][yy];
4072 is_player = ELEM_IS_PLAYER(content);
4074 if (is_player && (found_rating < 2 ||
4075 (found_rating == 2 && element < found_element)))
4077 start_x = x + xx - 1;
4078 start_y = y + yy - 1;
4081 found_element = element;
4084 if (!CAN_CHANGE(element))
4087 for (i = 0; i < element_info[element].num_change_pages; i++)
4089 /* check for player created from custom element as extended target */
4091 element_info[element].change_page[i].target_content.e[xx][yy];
4093 is_player = ELEM_IS_PLAYER(content);
4095 if (is_player && (found_rating < 1 ||
4096 (found_rating == 1 && element < found_element)))
4098 start_x = x + xx - 1;
4099 start_y = y + yy - 1;
4102 found_element = element;
4108 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4109 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4112 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4113 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4118 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4119 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4120 local_player->jx - MIDPOSX);
4122 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4123 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4124 local_player->jy - MIDPOSY);
4127 /* do not use PLAYING mask for fading out from main screen */
4128 game_status = GAME_MODE_MAIN;
4132 if (!game.restart_level)
4133 CloseDoor(DOOR_CLOSE_1);
4136 if (level_editor_test_game)
4137 FadeSkipNextFadeIn();
4139 FadeSetEnterScreen();
4141 if (level_editor_test_game)
4142 fading = fading_none;
4144 fading = menu.destination;
4148 FadeOut(REDRAW_FIELD);
4151 FadeOut(REDRAW_FIELD);
4154 game_status = GAME_MODE_PLAYING;
4156 /* !!! FIX THIS (START) !!! */
4157 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4159 InitGameEngine_EM();
4161 /* blit playfield from scroll buffer to normal back buffer for fading in */
4162 BlitScreenToBitmap_EM(backbuffer);
4169 /* after drawing the level, correct some elements */
4170 if (game.timegate_time_left == 0)
4171 CloseAllOpenTimegates();
4173 /* blit playfield from scroll buffer to normal back buffer for fading in */
4174 if (setup.soft_scrolling)
4175 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4177 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4179 /* !!! FIX THIS (END) !!! */
4182 FadeIn(REDRAW_FIELD);
4185 FadeIn(REDRAW_FIELD);
4190 if (!game.restart_level)
4192 /* copy default game door content to main double buffer */
4193 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4194 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4197 SetPanelBackground();
4198 SetDrawBackgroundMask(REDRAW_DOOR_1);
4200 UpdateGameDoorValues();
4201 DrawGameDoorValues();
4203 if (!game.restart_level)
4207 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4208 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4209 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4213 /* copy actual game door content to door double buffer for OpenDoor() */
4214 BlitBitmap(drawto, bitmap_db_door,
4215 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4217 OpenDoor(DOOR_OPEN_ALL);
4219 PlaySound(SND_GAME_STARTING);
4221 if (setup.sound_music)
4224 KeyboardAutoRepeatOffUnlessAutoplay();
4228 for (i = 0; i < MAX_PLAYERS; i++)
4229 printf("Player %d %sactive.\n",
4230 i + 1, (stored_player[i].active ? "" : "not "));
4241 game.restart_level = FALSE;
4244 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4246 /* this is used for non-R'n'D game engines to update certain engine values */
4248 /* needed to determine if sounds are played within the visible screen area */
4249 scroll_x = actual_scroll_x;
4250 scroll_y = actual_scroll_y;
4253 void InitMovDir(int x, int y)
4255 int i, element = Feld[x][y];
4256 static int xy[4][2] =
4263 static int direction[3][4] =
4265 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4266 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4267 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4276 Feld[x][y] = EL_BUG;
4277 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4280 case EL_SPACESHIP_RIGHT:
4281 case EL_SPACESHIP_UP:
4282 case EL_SPACESHIP_LEFT:
4283 case EL_SPACESHIP_DOWN:
4284 Feld[x][y] = EL_SPACESHIP;
4285 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4288 case EL_BD_BUTTERFLY_RIGHT:
4289 case EL_BD_BUTTERFLY_UP:
4290 case EL_BD_BUTTERFLY_LEFT:
4291 case EL_BD_BUTTERFLY_DOWN:
4292 Feld[x][y] = EL_BD_BUTTERFLY;
4293 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4296 case EL_BD_FIREFLY_RIGHT:
4297 case EL_BD_FIREFLY_UP:
4298 case EL_BD_FIREFLY_LEFT:
4299 case EL_BD_FIREFLY_DOWN:
4300 Feld[x][y] = EL_BD_FIREFLY;
4301 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4304 case EL_PACMAN_RIGHT:
4306 case EL_PACMAN_LEFT:
4307 case EL_PACMAN_DOWN:
4308 Feld[x][y] = EL_PACMAN;
4309 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4312 case EL_YAMYAM_LEFT:
4313 case EL_YAMYAM_RIGHT:
4315 case EL_YAMYAM_DOWN:
4316 Feld[x][y] = EL_YAMYAM;
4317 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4320 case EL_SP_SNIKSNAK:
4321 MovDir[x][y] = MV_UP;
4324 case EL_SP_ELECTRON:
4325 MovDir[x][y] = MV_LEFT;
4332 Feld[x][y] = EL_MOLE;
4333 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4337 if (IS_CUSTOM_ELEMENT(element))
4339 struct ElementInfo *ei = &element_info[element];
4340 int move_direction_initial = ei->move_direction_initial;
4341 int move_pattern = ei->move_pattern;
4343 if (move_direction_initial == MV_START_PREVIOUS)
4345 if (MovDir[x][y] != MV_NONE)
4348 move_direction_initial = MV_START_AUTOMATIC;
4351 if (move_direction_initial == MV_START_RANDOM)
4352 MovDir[x][y] = 1 << RND(4);
4353 else if (move_direction_initial & MV_ANY_DIRECTION)
4354 MovDir[x][y] = move_direction_initial;
4355 else if (move_pattern == MV_ALL_DIRECTIONS ||
4356 move_pattern == MV_TURNING_LEFT ||
4357 move_pattern == MV_TURNING_RIGHT ||
4358 move_pattern == MV_TURNING_LEFT_RIGHT ||
4359 move_pattern == MV_TURNING_RIGHT_LEFT ||
4360 move_pattern == MV_TURNING_RANDOM)
4361 MovDir[x][y] = 1 << RND(4);
4362 else if (move_pattern == MV_HORIZONTAL)
4363 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4364 else if (move_pattern == MV_VERTICAL)
4365 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4366 else if (move_pattern & MV_ANY_DIRECTION)
4367 MovDir[x][y] = element_info[element].move_pattern;
4368 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4369 move_pattern == MV_ALONG_RIGHT_SIDE)
4371 /* use random direction as default start direction */
4372 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4373 MovDir[x][y] = 1 << RND(4);
4375 for (i = 0; i < NUM_DIRECTIONS; i++)
4377 int x1 = x + xy[i][0];
4378 int y1 = y + xy[i][1];
4380 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4382 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4383 MovDir[x][y] = direction[0][i];
4385 MovDir[x][y] = direction[1][i];
4394 MovDir[x][y] = 1 << RND(4);
4396 if (element != EL_BUG &&
4397 element != EL_SPACESHIP &&
4398 element != EL_BD_BUTTERFLY &&
4399 element != EL_BD_FIREFLY)
4402 for (i = 0; i < NUM_DIRECTIONS; i++)
4404 int x1 = x + xy[i][0];
4405 int y1 = y + xy[i][1];
4407 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4409 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4411 MovDir[x][y] = direction[0][i];
4414 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4415 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4417 MovDir[x][y] = direction[1][i];
4426 GfxDir[x][y] = MovDir[x][y];
4429 void InitAmoebaNr(int x, int y)
4432 int group_nr = AmoebeNachbarNr(x, y);
4436 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4438 if (AmoebaCnt[i] == 0)
4446 AmoebaNr[x][y] = group_nr;
4447 AmoebaCnt[group_nr]++;
4448 AmoebaCnt2[group_nr]++;
4451 static void PlayerWins(struct PlayerInfo *player)
4453 player->LevelSolved = TRUE;
4454 player->GameOver = TRUE;
4456 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4457 level.native_em_level->lev->score : player->score);
4459 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4460 player->LevelSolved_CountingScore = player->score_final;
4465 static int time, time_final;
4466 static int score, score_final;
4467 static int game_over_delay_1 = 0;
4468 static int game_over_delay_2 = 0;
4469 int game_over_delay_value_1 = 50;
4470 int game_over_delay_value_2 = 50;
4472 if (!local_player->LevelSolved_GameWon)
4476 /* do not start end game actions before the player stops moving (to exit) */
4477 if (local_player->MovPos)
4480 local_player->LevelSolved_GameWon = TRUE;
4481 local_player->LevelSolved_SaveTape = tape.recording;
4482 local_player->LevelSolved_SaveScore = !tape.playing;
4484 if (tape.auto_play) /* tape might already be stopped here */
4485 tape.auto_play_level_solved = TRUE;
4491 game_over_delay_1 = game_over_delay_value_1;
4492 game_over_delay_2 = game_over_delay_value_2;
4494 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4495 score = score_final = local_player->score_final;
4500 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4502 else if (level.time == 0 && TimePlayed < 999)
4505 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4508 local_player->score_final = score_final;
4510 if (level_editor_test_game)
4513 score = score_final;
4516 local_player->LevelSolved_CountingTime = time;
4517 local_player->LevelSolved_CountingScore = score;
4519 game_panel_controls[GAME_PANEL_TIME].value = time;
4520 game_panel_controls[GAME_PANEL_SCORE].value = score;
4522 DisplayGameControlValues();
4524 DrawGameValue_Time(time);
4525 DrawGameValue_Score(score);
4529 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4531 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4533 /* close exit door after last player */
4534 if ((AllPlayersGone &&
4535 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4536 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4537 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4538 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4539 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4541 int element = Feld[ExitX][ExitY];
4544 if (element == EL_EM_EXIT_OPEN ||
4545 element == EL_EM_STEEL_EXIT_OPEN)
4552 Feld[ExitX][ExitY] =
4553 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4554 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4555 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4556 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4557 EL_EM_STEEL_EXIT_CLOSING);
4559 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4563 /* player disappears */
4564 DrawLevelField(ExitX, ExitY);
4567 for (i = 0; i < MAX_PLAYERS; i++)
4569 struct PlayerInfo *player = &stored_player[i];
4571 if (player->present)
4573 RemovePlayer(player);
4575 /* player disappears */
4576 DrawLevelField(player->jx, player->jy);
4581 PlaySound(SND_GAME_WINNING);
4584 if (game_over_delay_1 > 0)
4586 game_over_delay_1--;
4591 if (time != time_final)
4593 int time_to_go = ABS(time_final - time);
4594 int time_count_dir = (time < time_final ? +1 : -1);
4595 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4597 time += time_count_steps * time_count_dir;
4598 score += time_count_steps * level.score[SC_TIME_BONUS];
4601 local_player->LevelSolved_CountingTime = time;
4602 local_player->LevelSolved_CountingScore = score;
4604 game_panel_controls[GAME_PANEL_TIME].value = time;
4605 game_panel_controls[GAME_PANEL_SCORE].value = score;
4607 DisplayGameControlValues();
4609 DrawGameValue_Time(time);
4610 DrawGameValue_Score(score);
4613 if (time == time_final)
4614 StopSound(SND_GAME_LEVELTIME_BONUS);
4615 else if (setup.sound_loops)
4616 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4618 PlaySound(SND_GAME_LEVELTIME_BONUS);
4623 local_player->LevelSolved_PanelOff = TRUE;
4625 if (game_over_delay_2 > 0)
4627 game_over_delay_2--;
4640 boolean raise_level = FALSE;
4642 local_player->LevelSolved_GameEnd = TRUE;
4644 CloseDoor(DOOR_CLOSE_1);
4646 if (local_player->LevelSolved_SaveTape)
4653 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4655 SaveTape(tape.level_nr); /* ask to save tape */
4659 if (level_editor_test_game)
4661 game_status = GAME_MODE_MAIN;
4664 DrawAndFadeInMainMenu(REDRAW_FIELD);
4672 if (!local_player->LevelSolved_SaveScore)
4675 FadeOut(REDRAW_FIELD);
4678 game_status = GAME_MODE_MAIN;
4680 DrawAndFadeInMainMenu(REDRAW_FIELD);
4685 if (level_nr == leveldir_current->handicap_level)
4687 leveldir_current->handicap_level++;
4688 SaveLevelSetup_SeriesInfo();
4691 if (level_nr < leveldir_current->last_level)
4692 raise_level = TRUE; /* advance to next level */
4694 if ((hi_pos = NewHiScore()) >= 0)
4696 game_status = GAME_MODE_SCORES;
4698 DrawHallOfFame(hi_pos);
4709 FadeOut(REDRAW_FIELD);
4712 game_status = GAME_MODE_MAIN;
4720 DrawAndFadeInMainMenu(REDRAW_FIELD);
4729 LoadScore(level_nr);
4731 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4732 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4735 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4737 if (local_player->score_final > highscore[k].Score)
4739 /* player has made it to the hall of fame */
4741 if (k < MAX_SCORE_ENTRIES - 1)
4743 int m = MAX_SCORE_ENTRIES - 1;
4746 for (l = k; l < MAX_SCORE_ENTRIES; l++)
4747 if (strEqual(setup.player_name, highscore[l].Name))
4749 if (m == k) /* player's new highscore overwrites his old one */
4753 for (l = m; l > k; l--)
4755 strcpy(highscore[l].Name, highscore[l - 1].Name);
4756 highscore[l].Score = highscore[l - 1].Score;
4763 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4764 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4765 highscore[k].Score = local_player->score_final;
4771 else if (!strncmp(setup.player_name, highscore[k].Name,
4772 MAX_PLAYER_NAME_LEN))
4773 break; /* player already there with a higher score */
4779 SaveScore(level_nr);
4784 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4786 int element = Feld[x][y];
4787 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4788 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4789 int horiz_move = (dx != 0);
4790 int sign = (horiz_move ? dx : dy);
4791 int step = sign * element_info[element].move_stepsize;
4793 /* special values for move stepsize for spring and things on conveyor belt */
4796 if (CAN_FALL(element) &&
4797 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4798 step = sign * MOVE_STEPSIZE_NORMAL / 2;
4799 else if (element == EL_SPRING)
4800 step = sign * MOVE_STEPSIZE_NORMAL * 2;
4806 inline static int getElementMoveStepsize(int x, int y)
4808 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4811 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4813 if (player->GfxAction != action || player->GfxDir != dir)
4816 printf("Player frame reset! (%d => %d, %d => %d)\n",
4817 player->GfxAction, action, player->GfxDir, dir);
4820 player->GfxAction = action;
4821 player->GfxDir = dir;
4823 player->StepFrame = 0;
4827 #if USE_GFX_RESET_GFX_ANIMATION
4828 static void ResetGfxFrame(int x, int y, boolean redraw)
4830 int element = Feld[x][y];
4831 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4832 int last_gfx_frame = GfxFrame[x][y];
4834 if (graphic_info[graphic].anim_global_sync)
4835 GfxFrame[x][y] = FrameCounter;
4836 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4837 GfxFrame[x][y] = CustomValue[x][y];
4838 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4839 GfxFrame[x][y] = element_info[element].collect_score;
4840 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4841 GfxFrame[x][y] = ChangeDelay[x][y];
4843 if (redraw && GfxFrame[x][y] != last_gfx_frame)
4844 DrawLevelGraphicAnimation(x, y, graphic);
4848 static void ResetGfxAnimation(int x, int y)
4850 GfxAction[x][y] = ACTION_DEFAULT;
4851 GfxDir[x][y] = MovDir[x][y];
4854 #if USE_GFX_RESET_GFX_ANIMATION
4855 ResetGfxFrame(x, y, FALSE);
4859 static void ResetRandomAnimationValue(int x, int y)
4861 GfxRandom[x][y] = INIT_GFX_RANDOM();
4864 void InitMovingField(int x, int y, int direction)
4866 int element = Feld[x][y];
4867 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4868 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
4871 boolean is_moving_before, is_moving_after;
4873 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4876 /* check if element was/is moving or being moved before/after mode change */
4879 is_moving_before = (WasJustMoving[x][y] != 0);
4881 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4882 is_moving_before = WasJustMoving[x][y];
4885 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4887 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
4889 /* reset animation only for moving elements which change direction of moving
4890 or which just started or stopped moving
4891 (else CEs with property "can move" / "not moving" are reset each frame) */
4892 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4894 if (is_moving_before != is_moving_after ||
4895 direction != MovDir[x][y])
4896 ResetGfxAnimation(x, y);
4898 if ((is_moving_before || is_moving_after) && !continues_moving)
4899 ResetGfxAnimation(x, y);
4902 if (!continues_moving)
4903 ResetGfxAnimation(x, y);
4906 MovDir[x][y] = direction;
4907 GfxDir[x][y] = direction;
4909 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4910 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4911 direction == MV_DOWN && CAN_FALL(element) ?
4912 ACTION_FALLING : ACTION_MOVING);
4914 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4915 ACTION_FALLING : ACTION_MOVING);
4918 /* this is needed for CEs with property "can move" / "not moving" */
4920 if (is_moving_after)
4922 if (Feld[newx][newy] == EL_EMPTY)
4923 Feld[newx][newy] = EL_BLOCKED;
4925 MovDir[newx][newy] = MovDir[x][y];
4927 #if USE_NEW_CUSTOM_VALUE
4928 CustomValue[newx][newy] = CustomValue[x][y];
4931 GfxFrame[newx][newy] = GfxFrame[x][y];
4932 GfxRandom[newx][newy] = GfxRandom[x][y];
4933 GfxAction[newx][newy] = GfxAction[x][y];
4934 GfxDir[newx][newy] = GfxDir[x][y];
4938 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4940 int direction = MovDir[x][y];
4941 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4942 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
4948 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4950 int oldx = x, oldy = y;
4951 int direction = MovDir[x][y];
4953 if (direction == MV_LEFT)
4955 else if (direction == MV_RIGHT)
4957 else if (direction == MV_UP)
4959 else if (direction == MV_DOWN)
4962 *comes_from_x = oldx;
4963 *comes_from_y = oldy;
4966 int MovingOrBlocked2Element(int x, int y)
4968 int element = Feld[x][y];
4970 if (element == EL_BLOCKED)
4974 Blocked2Moving(x, y, &oldx, &oldy);
4975 return Feld[oldx][oldy];
4981 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4983 /* like MovingOrBlocked2Element(), but if element is moving
4984 and (x,y) is the field the moving element is just leaving,
4985 return EL_BLOCKED instead of the element value */
4986 int element = Feld[x][y];
4988 if (IS_MOVING(x, y))
4990 if (element == EL_BLOCKED)
4994 Blocked2Moving(x, y, &oldx, &oldy);
4995 return Feld[oldx][oldy];
5004 static void RemoveField(int x, int y)
5006 Feld[x][y] = EL_EMPTY;
5012 #if USE_NEW_CUSTOM_VALUE
5013 CustomValue[x][y] = 0;
5017 ChangeDelay[x][y] = 0;
5018 ChangePage[x][y] = -1;
5019 Pushed[x][y] = FALSE;
5022 ExplodeField[x][y] = EX_TYPE_NONE;
5025 GfxElement[x][y] = EL_UNDEFINED;
5026 GfxAction[x][y] = ACTION_DEFAULT;
5027 GfxDir[x][y] = MV_NONE;
5030 void RemoveMovingField(int x, int y)
5032 int oldx = x, oldy = y, newx = x, newy = y;
5033 int element = Feld[x][y];
5034 int next_element = EL_UNDEFINED;
5036 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5039 if (IS_MOVING(x, y))
5041 Moving2Blocked(x, y, &newx, &newy);
5043 if (Feld[newx][newy] != EL_BLOCKED)
5045 /* element is moving, but target field is not free (blocked), but
5046 already occupied by something different (example: acid pool);
5047 in this case, only remove the moving field, but not the target */
5049 RemoveField(oldx, oldy);
5051 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5053 DrawLevelField(oldx, oldy);
5058 else if (element == EL_BLOCKED)
5060 Blocked2Moving(x, y, &oldx, &oldy);
5061 if (!IS_MOVING(oldx, oldy))
5065 if (element == EL_BLOCKED &&
5066 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5067 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5068 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5069 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5070 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5071 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5072 next_element = get_next_element(Feld[oldx][oldy]);
5074 RemoveField(oldx, oldy);
5075 RemoveField(newx, newy);
5077 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5079 if (next_element != EL_UNDEFINED)
5080 Feld[oldx][oldy] = next_element;
5082 DrawLevelField(oldx, oldy);
5083 DrawLevelField(newx, newy);
5086 void DrawDynamite(int x, int y)
5088 int sx = SCREENX(x), sy = SCREENY(y);
5089 int graphic = el2img(Feld[x][y]);
5092 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5095 if (IS_WALKABLE_INSIDE(Back[x][y]))
5099 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5100 else if (Store[x][y])
5101 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5103 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5105 if (Back[x][y] || Store[x][y])
5106 DrawGraphicThruMask(sx, sy, graphic, frame);
5108 DrawGraphic(sx, sy, graphic, frame);
5111 void CheckDynamite(int x, int y)
5113 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5117 if (MovDelay[x][y] != 0)
5120 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5126 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5131 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5133 boolean num_checked_players = 0;
5136 for (i = 0; i < MAX_PLAYERS; i++)
5138 if (stored_player[i].active)
5140 int sx = stored_player[i].jx;
5141 int sy = stored_player[i].jy;
5143 if (num_checked_players == 0)
5150 *sx1 = MIN(*sx1, sx);
5151 *sy1 = MIN(*sy1, sy);
5152 *sx2 = MAX(*sx2, sx);
5153 *sy2 = MAX(*sy2, sy);
5156 num_checked_players++;
5161 static boolean checkIfAllPlayersFitToScreen_RND()
5163 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5165 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5167 return (sx2 - sx1 < SCR_FIELDX &&
5168 sy2 - sy1 < SCR_FIELDY);
5171 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5173 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5175 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5177 *sx = (sx1 + sx2) / 2;
5178 *sy = (sy1 + sy2) / 2;
5181 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5182 boolean center_screen, boolean quick_relocation)
5184 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5185 boolean no_delay = (tape.warp_forward);
5186 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5187 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5189 if (quick_relocation)
5191 int offset = game.scroll_delay_value;
5193 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5195 if (!level.shifted_relocation || center_screen)
5197 /* quick relocation (without scrolling), with centering of screen */
5199 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5200 x > SBX_Right + MIDPOSX ? SBX_Right :
5203 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5204 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5209 /* quick relocation (without scrolling), but do not center screen */
5211 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5212 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5215 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5216 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5219 int offset_x = x + (scroll_x - center_scroll_x);
5220 int offset_y = y + (scroll_y - center_scroll_y);
5222 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5223 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5224 offset_x - MIDPOSX);
5226 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5227 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5228 offset_y - MIDPOSY);
5233 /* quick relocation (without scrolling), inside visible screen area */
5235 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5236 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5237 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5239 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5240 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5241 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5243 /* don't scroll over playfield boundaries */
5244 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5245 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5247 /* don't scroll over playfield boundaries */
5248 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5249 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5252 RedrawPlayfield(TRUE, 0,0,0,0);
5257 int scroll_xx, scroll_yy;
5259 if (!level.shifted_relocation || center_screen)
5261 /* visible relocation (with scrolling), with centering of screen */
5263 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5264 x > SBX_Right + MIDPOSX ? SBX_Right :
5267 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5268 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5273 /* visible relocation (with scrolling), but do not center screen */
5275 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5276 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5279 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5280 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5283 int offset_x = x + (scroll_x - center_scroll_x);
5284 int offset_y = y + (scroll_y - center_scroll_y);
5286 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5287 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5288 offset_x - MIDPOSX);
5290 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5291 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5292 offset_y - MIDPOSY);
5297 /* visible relocation (with scrolling), with centering of screen */
5299 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5300 x > SBX_Right + MIDPOSX ? SBX_Right :
5303 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5304 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5308 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5310 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5313 int fx = FX, fy = FY;
5315 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5316 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5318 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5324 fx += dx * TILEX / 2;
5325 fy += dy * TILEY / 2;
5327 ScrollLevel(dx, dy);
5330 /* scroll in two steps of half tile size to make things smoother */
5331 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5333 Delay(wait_delay_value);
5335 /* scroll second step to align at full tile size */
5337 Delay(wait_delay_value);
5342 Delay(wait_delay_value);
5346 void RelocatePlayer(int jx, int jy, int el_player_raw)
5348 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5349 int player_nr = GET_PLAYER_NR(el_player);
5350 struct PlayerInfo *player = &stored_player[player_nr];
5351 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5352 boolean no_delay = (tape.warp_forward);
5353 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5354 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5355 int old_jx = player->jx;
5356 int old_jy = player->jy;
5357 int old_element = Feld[old_jx][old_jy];
5358 int element = Feld[jx][jy];
5359 boolean player_relocated = (old_jx != jx || old_jy != jy);
5361 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5362 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5363 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5364 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5365 int leave_side_horiz = move_dir_horiz;
5366 int leave_side_vert = move_dir_vert;
5367 int enter_side = enter_side_horiz | enter_side_vert;
5368 int leave_side = leave_side_horiz | leave_side_vert;
5370 if (player->GameOver) /* do not reanimate dead player */
5373 if (!player_relocated) /* no need to relocate the player */
5376 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5378 RemoveField(jx, jy); /* temporarily remove newly placed player */
5379 DrawLevelField(jx, jy);
5382 if (player->present)
5384 while (player->MovPos)
5386 ScrollPlayer(player, SCROLL_GO_ON);
5387 ScrollScreen(NULL, SCROLL_GO_ON);
5389 AdvanceFrameAndPlayerCounters(player->index_nr);
5394 Delay(wait_delay_value);
5397 DrawPlayer(player); /* needed here only to cleanup last field */
5398 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5400 player->is_moving = FALSE;
5403 if (IS_CUSTOM_ELEMENT(old_element))
5404 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5406 player->index_bit, leave_side);
5408 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5410 player->index_bit, leave_side);
5412 Feld[jx][jy] = el_player;
5413 InitPlayerField(jx, jy, el_player, TRUE);
5415 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5417 Feld[jx][jy] = element;
5418 InitField(jx, jy, FALSE);
5421 /* only visually relocate centered player */
5422 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5423 FALSE, level.instant_relocation);
5425 TestIfPlayerTouchesBadThing(jx, jy);
5426 TestIfPlayerTouchesCustomElement(jx, jy);
5428 if (IS_CUSTOM_ELEMENT(element))
5429 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5430 player->index_bit, enter_side);
5432 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5433 player->index_bit, enter_side);
5436 void Explode(int ex, int ey, int phase, int mode)
5442 /* !!! eliminate this variable !!! */
5443 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5445 if (game.explosions_delayed)
5447 ExplodeField[ex][ey] = mode;
5451 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5453 int center_element = Feld[ex][ey];
5454 int artwork_element, explosion_element; /* set these values later */
5457 /* --- This is only really needed (and now handled) in "Impact()". --- */
5458 /* do not explode moving elements that left the explode field in time */
5459 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5460 center_element == EL_EMPTY &&
5461 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5466 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5467 if (mode == EX_TYPE_NORMAL ||
5468 mode == EX_TYPE_CENTER ||
5469 mode == EX_TYPE_CROSS)
5470 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5473 /* remove things displayed in background while burning dynamite */
5474 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5477 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5479 /* put moving element to center field (and let it explode there) */
5480 center_element = MovingOrBlocked2Element(ex, ey);
5481 RemoveMovingField(ex, ey);
5482 Feld[ex][ey] = center_element;
5485 /* now "center_element" is finally determined -- set related values now */
5486 artwork_element = center_element; /* for custom player artwork */
5487 explosion_element = center_element; /* for custom player artwork */
5489 if (IS_PLAYER(ex, ey))
5491 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5493 artwork_element = stored_player[player_nr].artwork_element;
5495 if (level.use_explosion_element[player_nr])
5497 explosion_element = level.explosion_element[player_nr];
5498 artwork_element = explosion_element;
5503 if (mode == EX_TYPE_NORMAL ||
5504 mode == EX_TYPE_CENTER ||
5505 mode == EX_TYPE_CROSS)
5506 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5509 last_phase = element_info[explosion_element].explosion_delay + 1;
5511 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5513 int xx = x - ex + 1;
5514 int yy = y - ey + 1;
5517 if (!IN_LEV_FIELD(x, y) ||
5518 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5519 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5522 element = Feld[x][y];
5524 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5526 element = MovingOrBlocked2Element(x, y);
5528 if (!IS_EXPLOSION_PROOF(element))
5529 RemoveMovingField(x, y);
5532 /* indestructible elements can only explode in center (but not flames) */
5533 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5534 mode == EX_TYPE_BORDER)) ||
5535 element == EL_FLAMES)
5538 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5539 behaviour, for example when touching a yamyam that explodes to rocks
5540 with active deadly shield, a rock is created under the player !!! */
5541 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5543 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5544 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5545 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5547 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5550 if (IS_ACTIVE_BOMB(element))
5552 /* re-activate things under the bomb like gate or penguin */
5553 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5560 /* save walkable background elements while explosion on same tile */
5561 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5562 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5563 Back[x][y] = element;
5565 /* ignite explodable elements reached by other explosion */
5566 if (element == EL_EXPLOSION)
5567 element = Store2[x][y];
5569 if (AmoebaNr[x][y] &&
5570 (element == EL_AMOEBA_FULL ||
5571 element == EL_BD_AMOEBA ||
5572 element == EL_AMOEBA_GROWING))
5574 AmoebaCnt[AmoebaNr[x][y]]--;
5575 AmoebaCnt2[AmoebaNr[x][y]]--;
5580 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5582 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5584 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5586 if (PLAYERINFO(ex, ey)->use_murphy)
5587 Store[x][y] = EL_EMPTY;
5590 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5591 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5592 else if (ELEM_IS_PLAYER(center_element))
5593 Store[x][y] = EL_EMPTY;
5594 else if (center_element == EL_YAMYAM)
5595 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5596 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5597 Store[x][y] = element_info[center_element].content.e[xx][yy];
5599 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5600 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5601 otherwise) -- FIX THIS !!! */
5602 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5603 Store[x][y] = element_info[element].content.e[1][1];
5605 else if (!CAN_EXPLODE(element))
5606 Store[x][y] = element_info[element].content.e[1][1];
5609 Store[x][y] = EL_EMPTY;
5611 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5612 center_element == EL_AMOEBA_TO_DIAMOND)
5613 Store2[x][y] = element;
5615 Feld[x][y] = EL_EXPLOSION;
5616 GfxElement[x][y] = artwork_element;
5618 ExplodePhase[x][y] = 1;
5619 ExplodeDelay[x][y] = last_phase;
5624 if (center_element == EL_YAMYAM)
5625 game.yamyam_content_nr =
5626 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5638 GfxFrame[x][y] = 0; /* restart explosion animation */
5640 last_phase = ExplodeDelay[x][y];
5642 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5646 /* activate this even in non-DEBUG version until cause for crash in
5647 getGraphicAnimationFrame() (see below) is found and eliminated */
5653 /* this can happen if the player leaves an explosion just in time */
5654 if (GfxElement[x][y] == EL_UNDEFINED)
5655 GfxElement[x][y] = EL_EMPTY;
5657 if (GfxElement[x][y] == EL_UNDEFINED)
5660 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5661 printf("Explode(): This should never happen!\n");
5664 GfxElement[x][y] = EL_EMPTY;
5670 border_element = Store2[x][y];
5671 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5672 border_element = StorePlayer[x][y];
5674 if (phase == element_info[border_element].ignition_delay ||
5675 phase == last_phase)
5677 boolean border_explosion = FALSE;
5679 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5680 !PLAYER_EXPLOSION_PROTECTED(x, y))
5682 KillPlayerUnlessExplosionProtected(x, y);
5683 border_explosion = TRUE;
5685 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5687 Feld[x][y] = Store2[x][y];
5690 border_explosion = TRUE;
5692 else if (border_element == EL_AMOEBA_TO_DIAMOND)
5694 AmoebeUmwandeln(x, y);
5696 border_explosion = TRUE;
5699 /* if an element just explodes due to another explosion (chain-reaction),
5700 do not immediately end the new explosion when it was the last frame of
5701 the explosion (as it would be done in the following "if"-statement!) */
5702 if (border_explosion && phase == last_phase)
5706 if (phase == last_phase)
5710 element = Feld[x][y] = Store[x][y];
5711 Store[x][y] = Store2[x][y] = 0;
5712 GfxElement[x][y] = EL_UNDEFINED;
5714 /* player can escape from explosions and might therefore be still alive */
5715 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5716 element <= EL_PLAYER_IS_EXPLODING_4)
5718 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5719 int explosion_element = EL_PLAYER_1 + player_nr;
5720 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5721 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5723 if (level.use_explosion_element[player_nr])
5724 explosion_element = level.explosion_element[player_nr];
5726 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5727 element_info[explosion_element].content.e[xx][yy]);
5730 /* restore probably existing indestructible background element */
5731 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5732 element = Feld[x][y] = Back[x][y];
5735 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5736 GfxDir[x][y] = MV_NONE;
5737 ChangeDelay[x][y] = 0;
5738 ChangePage[x][y] = -1;
5740 #if USE_NEW_CUSTOM_VALUE
5741 CustomValue[x][y] = 0;
5744 InitField_WithBug2(x, y, FALSE);
5746 DrawLevelField(x, y);
5748 TestIfElementTouchesCustomElement(x, y);
5750 if (GFX_CRUMBLED(element))
5751 DrawLevelFieldCrumbledSandNeighbours(x, y);
5753 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5754 StorePlayer[x][y] = 0;
5756 if (ELEM_IS_PLAYER(element))
5757 RelocatePlayer(x, y, element);
5759 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5761 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5762 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5765 DrawLevelFieldCrumbledSand(x, y);
5767 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5769 DrawLevelElement(x, y, Back[x][y]);
5770 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5772 else if (IS_WALKABLE_UNDER(Back[x][y]))
5774 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5775 DrawLevelElementThruMask(x, y, Back[x][y]);
5777 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5778 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5782 void DynaExplode(int ex, int ey)
5785 int dynabomb_element = Feld[ex][ey];
5786 int dynabomb_size = 1;
5787 boolean dynabomb_xl = FALSE;
5788 struct PlayerInfo *player;
5789 static int xy[4][2] =
5797 if (IS_ACTIVE_BOMB(dynabomb_element))
5799 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5800 dynabomb_size = player->dynabomb_size;
5801 dynabomb_xl = player->dynabomb_xl;
5802 player->dynabombs_left++;
5805 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5807 for (i = 0; i < NUM_DIRECTIONS; i++)
5809 for (j = 1; j <= dynabomb_size; j++)
5811 int x = ex + j * xy[i][0];
5812 int y = ey + j * xy[i][1];
5815 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5818 element = Feld[x][y];
5820 /* do not restart explosions of fields with active bombs */
5821 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5824 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5826 if (element != EL_EMPTY && element != EL_EXPLOSION &&
5827 !IS_DIGGABLE(element) && !dynabomb_xl)
5833 void Bang(int x, int y)
5835 int element = MovingOrBlocked2Element(x, y);
5836 int explosion_type = EX_TYPE_NORMAL;
5838 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5840 struct PlayerInfo *player = PLAYERINFO(x, y);
5842 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5843 player->element_nr);
5845 if (level.use_explosion_element[player->index_nr])
5847 int explosion_element = level.explosion_element[player->index_nr];
5849 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5850 explosion_type = EX_TYPE_CROSS;
5851 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5852 explosion_type = EX_TYPE_CENTER;
5860 case EL_BD_BUTTERFLY:
5863 case EL_DARK_YAMYAM:
5867 RaiseScoreElement(element);
5870 case EL_DYNABOMB_PLAYER_1_ACTIVE:
5871 case EL_DYNABOMB_PLAYER_2_ACTIVE:
5872 case EL_DYNABOMB_PLAYER_3_ACTIVE:
5873 case EL_DYNABOMB_PLAYER_4_ACTIVE:
5874 case EL_DYNABOMB_INCREASE_NUMBER:
5875 case EL_DYNABOMB_INCREASE_SIZE:
5876 case EL_DYNABOMB_INCREASE_POWER:
5877 explosion_type = EX_TYPE_DYNA;
5880 case EL_DC_LANDMINE:
5882 case EL_EM_EXIT_OPEN:
5883 case EL_EM_STEEL_EXIT_OPEN:
5885 explosion_type = EX_TYPE_CENTER;
5890 case EL_LAMP_ACTIVE:
5891 case EL_AMOEBA_TO_DIAMOND:
5892 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
5893 explosion_type = EX_TYPE_CENTER;
5897 if (element_info[element].explosion_type == EXPLODES_CROSS)
5898 explosion_type = EX_TYPE_CROSS;
5899 else if (element_info[element].explosion_type == EXPLODES_1X1)
5900 explosion_type = EX_TYPE_CENTER;
5904 if (explosion_type == EX_TYPE_DYNA)
5907 Explode(x, y, EX_PHASE_START, explosion_type);
5909 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5912 void SplashAcid(int x, int y)
5914 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5915 (!IN_LEV_FIELD(x - 1, y - 2) ||
5916 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5917 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5919 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5920 (!IN_LEV_FIELD(x + 1, y - 2) ||
5921 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5922 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5924 PlayLevelSound(x, y, SND_ACID_SPLASHING);
5927 static void InitBeltMovement()
5929 static int belt_base_element[4] =
5931 EL_CONVEYOR_BELT_1_LEFT,
5932 EL_CONVEYOR_BELT_2_LEFT,
5933 EL_CONVEYOR_BELT_3_LEFT,
5934 EL_CONVEYOR_BELT_4_LEFT
5936 static int belt_base_active_element[4] =
5938 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5939 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5940 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5941 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5946 /* set frame order for belt animation graphic according to belt direction */
5947 for (i = 0; i < NUM_BELTS; i++)
5951 for (j = 0; j < NUM_BELT_PARTS; j++)
5953 int element = belt_base_active_element[belt_nr] + j;
5954 int graphic_1 = el2img(element);
5955 int graphic_2 = el2panelimg(element);
5957 if (game.belt_dir[i] == MV_LEFT)
5959 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5960 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5964 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
5965 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
5970 SCAN_PLAYFIELD(x, y)
5972 int element = Feld[x][y];
5974 for (i = 0; i < NUM_BELTS; i++)
5976 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5978 int e_belt_nr = getBeltNrFromBeltElement(element);
5981 if (e_belt_nr == belt_nr)
5983 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5985 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5992 static void ToggleBeltSwitch(int x, int y)
5994 static int belt_base_element[4] =
5996 EL_CONVEYOR_BELT_1_LEFT,
5997 EL_CONVEYOR_BELT_2_LEFT,
5998 EL_CONVEYOR_BELT_3_LEFT,
5999 EL_CONVEYOR_BELT_4_LEFT
6001 static int belt_base_active_element[4] =
6003 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6004 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6005 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6006 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6008 static int belt_base_switch_element[4] =
6010 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6011 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6012 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6013 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6015 static int belt_move_dir[4] =
6023 int element = Feld[x][y];
6024 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6025 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6026 int belt_dir = belt_move_dir[belt_dir_nr];
6029 if (!IS_BELT_SWITCH(element))
6032 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6033 game.belt_dir[belt_nr] = belt_dir;
6035 if (belt_dir_nr == 3)
6038 /* set frame order for belt animation graphic according to belt direction */
6039 for (i = 0; i < NUM_BELT_PARTS; i++)
6041 int element = belt_base_active_element[belt_nr] + i;
6042 int graphic_1 = el2img(element);
6043 int graphic_2 = el2panelimg(element);
6045 if (belt_dir == MV_LEFT)
6047 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6048 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6052 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6053 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6057 SCAN_PLAYFIELD(xx, yy)
6059 int element = Feld[xx][yy];
6061 if (IS_BELT_SWITCH(element))
6063 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6065 if (e_belt_nr == belt_nr)
6067 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6068 DrawLevelField(xx, yy);
6071 else if (IS_BELT(element) && belt_dir != MV_NONE)
6073 int e_belt_nr = getBeltNrFromBeltElement(element);
6075 if (e_belt_nr == belt_nr)
6077 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6079 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6080 DrawLevelField(xx, yy);
6083 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6085 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6087 if (e_belt_nr == belt_nr)
6089 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6091 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6092 DrawLevelField(xx, yy);
6098 static void ToggleSwitchgateSwitch(int x, int y)
6102 game.switchgate_pos = !game.switchgate_pos;
6104 SCAN_PLAYFIELD(xx, yy)
6106 int element = Feld[xx][yy];
6108 #if !USE_BOTH_SWITCHGATE_SWITCHES
6109 if (element == EL_SWITCHGATE_SWITCH_UP ||
6110 element == EL_SWITCHGATE_SWITCH_DOWN)
6112 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6113 DrawLevelField(xx, yy);
6115 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6116 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6118 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6119 DrawLevelField(xx, yy);
6122 if (element == EL_SWITCHGATE_SWITCH_UP)
6124 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6125 DrawLevelField(xx, yy);
6127 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6129 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6130 DrawLevelField(xx, yy);
6132 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6134 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6135 DrawLevelField(xx, yy);
6137 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6139 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6140 DrawLevelField(xx, yy);
6143 else if (element == EL_SWITCHGATE_OPEN ||
6144 element == EL_SWITCHGATE_OPENING)
6146 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6148 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6150 else if (element == EL_SWITCHGATE_CLOSED ||
6151 element == EL_SWITCHGATE_CLOSING)
6153 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6155 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6160 static int getInvisibleActiveFromInvisibleElement(int element)
6162 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6163 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6164 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6168 static int getInvisibleFromInvisibleActiveElement(int element)
6170 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6171 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6172 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6176 static void RedrawAllLightSwitchesAndInvisibleElements()
6180 SCAN_PLAYFIELD(x, y)
6182 int element = Feld[x][y];
6184 if (element == EL_LIGHT_SWITCH &&
6185 game.light_time_left > 0)
6187 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6188 DrawLevelField(x, y);
6190 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6191 game.light_time_left == 0)
6193 Feld[x][y] = EL_LIGHT_SWITCH;
6194 DrawLevelField(x, y);
6196 else if (element == EL_EMC_DRIPPER &&
6197 game.light_time_left > 0)
6199 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6200 DrawLevelField(x, y);
6202 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6203 game.light_time_left == 0)
6205 Feld[x][y] = EL_EMC_DRIPPER;
6206 DrawLevelField(x, y);
6208 else if (element == EL_INVISIBLE_STEELWALL ||
6209 element == EL_INVISIBLE_WALL ||
6210 element == EL_INVISIBLE_SAND)
6212 if (game.light_time_left > 0)
6213 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6215 DrawLevelField(x, y);
6217 /* uncrumble neighbour fields, if needed */
6218 if (element == EL_INVISIBLE_SAND)
6219 DrawLevelFieldCrumbledSandNeighbours(x, y);
6221 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6222 element == EL_INVISIBLE_WALL_ACTIVE ||
6223 element == EL_INVISIBLE_SAND_ACTIVE)
6225 if (game.light_time_left == 0)
6226 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6228 DrawLevelField(x, y);
6230 /* re-crumble neighbour fields, if needed */
6231 if (element == EL_INVISIBLE_SAND)
6232 DrawLevelFieldCrumbledSandNeighbours(x, y);
6237 static void RedrawAllInvisibleElementsForLenses()
6241 SCAN_PLAYFIELD(x, y)
6243 int element = Feld[x][y];
6245 if (element == EL_EMC_DRIPPER &&
6246 game.lenses_time_left > 0)
6248 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6249 DrawLevelField(x, y);
6251 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6252 game.lenses_time_left == 0)
6254 Feld[x][y] = EL_EMC_DRIPPER;
6255 DrawLevelField(x, y);
6257 else if (element == EL_INVISIBLE_STEELWALL ||
6258 element == EL_INVISIBLE_WALL ||
6259 element == EL_INVISIBLE_SAND)
6261 if (game.lenses_time_left > 0)
6262 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6264 DrawLevelField(x, y);
6266 /* uncrumble neighbour fields, if needed */
6267 if (element == EL_INVISIBLE_SAND)
6268 DrawLevelFieldCrumbledSandNeighbours(x, y);
6270 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6271 element == EL_INVISIBLE_WALL_ACTIVE ||
6272 element == EL_INVISIBLE_SAND_ACTIVE)
6274 if (game.lenses_time_left == 0)
6275 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6277 DrawLevelField(x, y);
6279 /* re-crumble neighbour fields, if needed */
6280 if (element == EL_INVISIBLE_SAND)
6281 DrawLevelFieldCrumbledSandNeighbours(x, y);
6286 static void RedrawAllInvisibleElementsForMagnifier()
6290 SCAN_PLAYFIELD(x, y)
6292 int element = Feld[x][y];
6294 if (element == EL_EMC_FAKE_GRASS &&
6295 game.magnify_time_left > 0)
6297 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6298 DrawLevelField(x, y);
6300 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6301 game.magnify_time_left == 0)
6303 Feld[x][y] = EL_EMC_FAKE_GRASS;
6304 DrawLevelField(x, y);
6306 else if (IS_GATE_GRAY(element) &&
6307 game.magnify_time_left > 0)
6309 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6310 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6311 IS_EM_GATE_GRAY(element) ?
6312 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6313 IS_EMC_GATE_GRAY(element) ?
6314 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6316 DrawLevelField(x, y);
6318 else if (IS_GATE_GRAY_ACTIVE(element) &&
6319 game.magnify_time_left == 0)
6321 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6322 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6323 IS_EM_GATE_GRAY_ACTIVE(element) ?
6324 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6325 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6326 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6328 DrawLevelField(x, y);
6333 static void ToggleLightSwitch(int x, int y)
6335 int element = Feld[x][y];
6337 game.light_time_left =
6338 (element == EL_LIGHT_SWITCH ?
6339 level.time_light * FRAMES_PER_SECOND : 0);
6341 RedrawAllLightSwitchesAndInvisibleElements();
6344 static void ActivateTimegateSwitch(int x, int y)
6348 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6350 SCAN_PLAYFIELD(xx, yy)
6352 int element = Feld[xx][yy];
6354 if (element == EL_TIMEGATE_CLOSED ||
6355 element == EL_TIMEGATE_CLOSING)
6357 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6358 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6362 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6364 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6365 DrawLevelField(xx, yy);
6372 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6373 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6375 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6379 void Impact(int x, int y)
6381 boolean last_line = (y == lev_fieldy - 1);
6382 boolean object_hit = FALSE;
6383 boolean impact = (last_line || object_hit);
6384 int element = Feld[x][y];
6385 int smashed = EL_STEELWALL;
6387 if (!last_line) /* check if element below was hit */
6389 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6392 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6393 MovDir[x][y + 1] != MV_DOWN ||
6394 MovPos[x][y + 1] <= TILEY / 2));
6396 /* do not smash moving elements that left the smashed field in time */
6397 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6398 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6401 #if USE_QUICKSAND_IMPACT_BUGFIX
6402 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6404 RemoveMovingField(x, y + 1);
6405 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6406 Feld[x][y + 2] = EL_ROCK;
6407 DrawLevelField(x, y + 2);
6412 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6414 RemoveMovingField(x, y + 1);
6415 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6416 Feld[x][y + 2] = EL_ROCK;
6417 DrawLevelField(x, y + 2);
6424 smashed = MovingOrBlocked2Element(x, y + 1);
6426 impact = (last_line || object_hit);
6429 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6431 SplashAcid(x, y + 1);
6435 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6436 /* only reset graphic animation if graphic really changes after impact */
6438 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6440 ResetGfxAnimation(x, y);
6441 DrawLevelField(x, y);
6444 if (impact && CAN_EXPLODE_IMPACT(element))
6449 else if (impact && element == EL_PEARL &&
6450 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6452 ResetGfxAnimation(x, y);
6454 Feld[x][y] = EL_PEARL_BREAKING;
6455 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6458 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6460 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6465 if (impact && element == EL_AMOEBA_DROP)
6467 if (object_hit && IS_PLAYER(x, y + 1))
6468 KillPlayerUnlessEnemyProtected(x, y + 1);
6469 else if (object_hit && smashed == EL_PENGUIN)
6473 Feld[x][y] = EL_AMOEBA_GROWING;
6474 Store[x][y] = EL_AMOEBA_WET;
6476 ResetRandomAnimationValue(x, y);
6481 if (object_hit) /* check which object was hit */
6483 if ((CAN_PASS_MAGIC_WALL(element) &&
6484 (smashed == EL_MAGIC_WALL ||
6485 smashed == EL_BD_MAGIC_WALL)) ||
6486 (CAN_PASS_DC_MAGIC_WALL(element) &&
6487 smashed == EL_DC_MAGIC_WALL))
6490 int activated_magic_wall =
6491 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6492 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6493 EL_DC_MAGIC_WALL_ACTIVE);
6495 /* activate magic wall / mill */
6496 SCAN_PLAYFIELD(xx, yy)
6498 if (Feld[xx][yy] == smashed)
6499 Feld[xx][yy] = activated_magic_wall;
6502 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6503 game.magic_wall_active = TRUE;
6505 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6506 SND_MAGIC_WALL_ACTIVATING :
6507 smashed == EL_BD_MAGIC_WALL ?
6508 SND_BD_MAGIC_WALL_ACTIVATING :
6509 SND_DC_MAGIC_WALL_ACTIVATING));
6512 if (IS_PLAYER(x, y + 1))
6514 if (CAN_SMASH_PLAYER(element))
6516 KillPlayerUnlessEnemyProtected(x, y + 1);
6520 else if (smashed == EL_PENGUIN)
6522 if (CAN_SMASH_PLAYER(element))
6528 else if (element == EL_BD_DIAMOND)
6530 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6536 else if (((element == EL_SP_INFOTRON ||
6537 element == EL_SP_ZONK) &&
6538 (smashed == EL_SP_SNIKSNAK ||
6539 smashed == EL_SP_ELECTRON ||
6540 smashed == EL_SP_DISK_ORANGE)) ||
6541 (element == EL_SP_INFOTRON &&
6542 smashed == EL_SP_DISK_YELLOW))
6547 else if (CAN_SMASH_EVERYTHING(element))
6549 if (IS_CLASSIC_ENEMY(smashed) ||
6550 CAN_EXPLODE_SMASHED(smashed))
6555 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6557 if (smashed == EL_LAMP ||
6558 smashed == EL_LAMP_ACTIVE)
6563 else if (smashed == EL_NUT)
6565 Feld[x][y + 1] = EL_NUT_BREAKING;
6566 PlayLevelSound(x, y, SND_NUT_BREAKING);
6567 RaiseScoreElement(EL_NUT);
6570 else if (smashed == EL_PEARL)
6572 ResetGfxAnimation(x, y);
6574 Feld[x][y + 1] = EL_PEARL_BREAKING;
6575 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6578 else if (smashed == EL_DIAMOND)
6580 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6581 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6584 else if (IS_BELT_SWITCH(smashed))
6586 ToggleBeltSwitch(x, y + 1);
6588 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6589 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6590 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6591 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6593 ToggleSwitchgateSwitch(x, y + 1);
6595 else if (smashed == EL_LIGHT_SWITCH ||
6596 smashed == EL_LIGHT_SWITCH_ACTIVE)
6598 ToggleLightSwitch(x, y + 1);
6603 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6606 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6608 CheckElementChangeBySide(x, y + 1, smashed, element,
6609 CE_SWITCHED, CH_SIDE_TOP);
6610 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6616 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6621 /* play sound of magic wall / mill */
6623 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6624 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6625 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6627 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6628 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6629 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6630 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6631 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6632 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6637 /* play sound of object that hits the ground */
6638 if (last_line || object_hit)
6639 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6642 inline static void TurnRoundExt(int x, int y)
6654 { 0, 0 }, { 0, 0 }, { 0, 0 },
6659 int left, right, back;
6663 { MV_DOWN, MV_UP, MV_RIGHT },
6664 { MV_UP, MV_DOWN, MV_LEFT },
6666 { MV_LEFT, MV_RIGHT, MV_DOWN },
6670 { MV_RIGHT, MV_LEFT, MV_UP }
6673 int element = Feld[x][y];
6674 int move_pattern = element_info[element].move_pattern;
6676 int old_move_dir = MovDir[x][y];
6677 int left_dir = turn[old_move_dir].left;
6678 int right_dir = turn[old_move_dir].right;
6679 int back_dir = turn[old_move_dir].back;
6681 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
6682 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
6683 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
6684 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
6686 int left_x = x + left_dx, left_y = y + left_dy;
6687 int right_x = x + right_dx, right_y = y + right_dy;
6688 int move_x = x + move_dx, move_y = y + move_dy;
6692 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6694 TestIfBadThingTouchesOtherBadThing(x, y);
6696 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6697 MovDir[x][y] = right_dir;
6698 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6699 MovDir[x][y] = left_dir;
6701 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6703 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
6706 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6708 TestIfBadThingTouchesOtherBadThing(x, y);
6710 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6711 MovDir[x][y] = left_dir;
6712 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6713 MovDir[x][y] = right_dir;
6715 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6717 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
6720 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6722 TestIfBadThingTouchesOtherBadThing(x, y);
6724 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6725 MovDir[x][y] = left_dir;
6726 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6727 MovDir[x][y] = right_dir;
6729 if (MovDir[x][y] != old_move_dir)
6732 else if (element == EL_YAMYAM)
6734 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6735 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6737 if (can_turn_left && can_turn_right)
6738 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6739 else if (can_turn_left)
6740 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6741 else if (can_turn_right)
6742 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6744 MovDir[x][y] = back_dir;
6746 MovDelay[x][y] = 16 + 16 * RND(3);
6748 else if (element == EL_DARK_YAMYAM)
6750 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6752 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6755 if (can_turn_left && can_turn_right)
6756 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6757 else if (can_turn_left)
6758 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6759 else if (can_turn_right)
6760 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6762 MovDir[x][y] = back_dir;
6764 MovDelay[x][y] = 16 + 16 * RND(3);
6766 else if (element == EL_PACMAN)
6768 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6769 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6771 if (can_turn_left && can_turn_right)
6772 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6773 else if (can_turn_left)
6774 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6775 else if (can_turn_right)
6776 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6778 MovDir[x][y] = back_dir;
6780 MovDelay[x][y] = 6 + RND(40);
6782 else if (element == EL_PIG)
6784 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6785 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6786 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6787 boolean should_turn_left, should_turn_right, should_move_on;
6789 int rnd = RND(rnd_value);
6791 should_turn_left = (can_turn_left &&
6793 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6794 y + back_dy + left_dy)));
6795 should_turn_right = (can_turn_right &&
6797 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6798 y + back_dy + right_dy)));
6799 should_move_on = (can_move_on &&
6802 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6803 y + move_dy + left_dy) ||
6804 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6805 y + move_dy + right_dy)));
6807 if (should_turn_left || should_turn_right || should_move_on)
6809 if (should_turn_left && should_turn_right && should_move_on)
6810 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
6811 rnd < 2 * rnd_value / 3 ? right_dir :
6813 else if (should_turn_left && should_turn_right)
6814 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6815 else if (should_turn_left && should_move_on)
6816 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6817 else if (should_turn_right && should_move_on)
6818 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6819 else if (should_turn_left)
6820 MovDir[x][y] = left_dir;
6821 else if (should_turn_right)
6822 MovDir[x][y] = right_dir;
6823 else if (should_move_on)
6824 MovDir[x][y] = old_move_dir;
6826 else if (can_move_on && rnd > rnd_value / 8)
6827 MovDir[x][y] = old_move_dir;
6828 else if (can_turn_left && can_turn_right)
6829 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6830 else if (can_turn_left && rnd > rnd_value / 8)
6831 MovDir[x][y] = left_dir;
6832 else if (can_turn_right && rnd > rnd_value/8)
6833 MovDir[x][y] = right_dir;
6835 MovDir[x][y] = back_dir;
6837 xx = x + move_xy[MovDir[x][y]].dx;
6838 yy = y + move_xy[MovDir[x][y]].dy;
6840 if (!IN_LEV_FIELD(xx, yy) ||
6841 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6842 MovDir[x][y] = old_move_dir;
6846 else if (element == EL_DRAGON)
6848 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6849 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6850 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6852 int rnd = RND(rnd_value);
6854 if (can_move_on && rnd > rnd_value / 8)
6855 MovDir[x][y] = old_move_dir;
6856 else if (can_turn_left && can_turn_right)
6857 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6858 else if (can_turn_left && rnd > rnd_value / 8)
6859 MovDir[x][y] = left_dir;
6860 else if (can_turn_right && rnd > rnd_value / 8)
6861 MovDir[x][y] = right_dir;
6863 MovDir[x][y] = back_dir;
6865 xx = x + move_xy[MovDir[x][y]].dx;
6866 yy = y + move_xy[MovDir[x][y]].dy;
6868 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6869 MovDir[x][y] = old_move_dir;
6873 else if (element == EL_MOLE)
6875 boolean can_move_on =
6876 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6877 IS_AMOEBOID(Feld[move_x][move_y]) ||
6878 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6881 boolean can_turn_left =
6882 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6883 IS_AMOEBOID(Feld[left_x][left_y])));
6885 boolean can_turn_right =
6886 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6887 IS_AMOEBOID(Feld[right_x][right_y])));
6889 if (can_turn_left && can_turn_right)
6890 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6891 else if (can_turn_left)
6892 MovDir[x][y] = left_dir;
6894 MovDir[x][y] = right_dir;
6897 if (MovDir[x][y] != old_move_dir)
6900 else if (element == EL_BALLOON)
6902 MovDir[x][y] = game.wind_direction;
6905 else if (element == EL_SPRING)
6907 #if USE_NEW_SPRING_BUMPER
6908 if (MovDir[x][y] & MV_HORIZONTAL)
6910 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6911 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6913 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6914 ResetGfxAnimation(move_x, move_y);
6915 DrawLevelField(move_x, move_y);
6917 MovDir[x][y] = back_dir;
6919 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6920 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6921 MovDir[x][y] = MV_NONE;
6924 if (MovDir[x][y] & MV_HORIZONTAL &&
6925 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6926 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6927 MovDir[x][y] = MV_NONE;
6932 else if (element == EL_ROBOT ||
6933 element == EL_SATELLITE ||
6934 element == EL_PENGUIN ||
6935 element == EL_EMC_ANDROID)
6937 int attr_x = -1, attr_y = -1;
6948 for (i = 0; i < MAX_PLAYERS; i++)
6950 struct PlayerInfo *player = &stored_player[i];
6951 int jx = player->jx, jy = player->jy;
6953 if (!player->active)
6957 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6965 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6966 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6967 game.engine_version < VERSION_IDENT(3,1,0,0)))
6973 if (element == EL_PENGUIN)
6976 static int xy[4][2] =
6984 for (i = 0; i < NUM_DIRECTIONS; i++)
6986 int ex = x + xy[i][0];
6987 int ey = y + xy[i][1];
6989 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6990 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6991 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6992 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7001 MovDir[x][y] = MV_NONE;
7003 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7004 else if (attr_x > x)
7005 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7007 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7008 else if (attr_y > y)
7009 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7011 if (element == EL_ROBOT)
7015 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7016 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7017 Moving2Blocked(x, y, &newx, &newy);
7019 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7020 MovDelay[x][y] = 8 + 8 * !RND(3);
7022 MovDelay[x][y] = 16;
7024 else if (element == EL_PENGUIN)
7030 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7032 boolean first_horiz = RND(2);
7033 int new_move_dir = MovDir[x][y];
7036 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7037 Moving2Blocked(x, y, &newx, &newy);
7039 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7043 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7044 Moving2Blocked(x, y, &newx, &newy);
7046 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7049 MovDir[x][y] = old_move_dir;
7053 else if (element == EL_SATELLITE)
7059 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7061 boolean first_horiz = RND(2);
7062 int new_move_dir = MovDir[x][y];
7065 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7066 Moving2Blocked(x, y, &newx, &newy);
7068 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7072 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7073 Moving2Blocked(x, y, &newx, &newy);
7075 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7078 MovDir[x][y] = old_move_dir;
7082 else if (element == EL_EMC_ANDROID)
7084 static int check_pos[16] =
7086 -1, /* 0 => (invalid) */
7087 7, /* 1 => MV_LEFT */
7088 3, /* 2 => MV_RIGHT */
7089 -1, /* 3 => (invalid) */
7091 0, /* 5 => MV_LEFT | MV_UP */
7092 2, /* 6 => MV_RIGHT | MV_UP */
7093 -1, /* 7 => (invalid) */
7094 5, /* 8 => MV_DOWN */
7095 6, /* 9 => MV_LEFT | MV_DOWN */
7096 4, /* 10 => MV_RIGHT | MV_DOWN */
7097 -1, /* 11 => (invalid) */
7098 -1, /* 12 => (invalid) */
7099 -1, /* 13 => (invalid) */
7100 -1, /* 14 => (invalid) */
7101 -1, /* 15 => (invalid) */
7109 { -1, -1, MV_LEFT | MV_UP },
7111 { +1, -1, MV_RIGHT | MV_UP },
7112 { +1, 0, MV_RIGHT },
7113 { +1, +1, MV_RIGHT | MV_DOWN },
7115 { -1, +1, MV_LEFT | MV_DOWN },
7118 int start_pos, check_order;
7119 boolean can_clone = FALSE;
7122 /* check if there is any free field around current position */
7123 for (i = 0; i < 8; i++)
7125 int newx = x + check_xy[i].dx;
7126 int newy = y + check_xy[i].dy;
7128 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7136 if (can_clone) /* randomly find an element to clone */
7140 start_pos = check_pos[RND(8)];
7141 check_order = (RND(2) ? -1 : +1);
7143 for (i = 0; i < 8; i++)
7145 int pos_raw = start_pos + i * check_order;
7146 int pos = (pos_raw + 8) % 8;
7147 int newx = x + check_xy[pos].dx;
7148 int newy = y + check_xy[pos].dy;
7150 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7152 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7153 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7155 Store[x][y] = Feld[newx][newy];
7164 if (can_clone) /* randomly find a direction to move */
7168 start_pos = check_pos[RND(8)];
7169 check_order = (RND(2) ? -1 : +1);
7171 for (i = 0; i < 8; i++)
7173 int pos_raw = start_pos + i * check_order;
7174 int pos = (pos_raw + 8) % 8;
7175 int newx = x + check_xy[pos].dx;
7176 int newy = y + check_xy[pos].dy;
7177 int new_move_dir = check_xy[pos].dir;
7179 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7181 MovDir[x][y] = new_move_dir;
7182 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7191 if (can_clone) /* cloning and moving successful */
7194 /* cannot clone -- try to move towards player */
7196 start_pos = check_pos[MovDir[x][y] & 0x0f];
7197 check_order = (RND(2) ? -1 : +1);
7199 for (i = 0; i < 3; i++)
7201 /* first check start_pos, then previous/next or (next/previous) pos */
7202 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7203 int pos = (pos_raw + 8) % 8;
7204 int newx = x + check_xy[pos].dx;
7205 int newy = y + check_xy[pos].dy;
7206 int new_move_dir = check_xy[pos].dir;
7208 if (IS_PLAYER(newx, newy))
7211 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7213 MovDir[x][y] = new_move_dir;
7214 MovDelay[x][y] = level.android_move_time * 8 + 1;
7221 else if (move_pattern == MV_TURNING_LEFT ||
7222 move_pattern == MV_TURNING_RIGHT ||
7223 move_pattern == MV_TURNING_LEFT_RIGHT ||
7224 move_pattern == MV_TURNING_RIGHT_LEFT ||
7225 move_pattern == MV_TURNING_RANDOM ||
7226 move_pattern == MV_ALL_DIRECTIONS)
7228 boolean can_turn_left =
7229 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7230 boolean can_turn_right =
7231 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7233 if (element_info[element].move_stepsize == 0) /* "not moving" */
7236 if (move_pattern == MV_TURNING_LEFT)
7237 MovDir[x][y] = left_dir;
7238 else if (move_pattern == MV_TURNING_RIGHT)
7239 MovDir[x][y] = right_dir;
7240 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7241 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7242 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7243 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7244 else if (move_pattern == MV_TURNING_RANDOM)
7245 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7246 can_turn_right && !can_turn_left ? right_dir :
7247 RND(2) ? left_dir : right_dir);
7248 else if (can_turn_left && can_turn_right)
7249 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7250 else if (can_turn_left)
7251 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7252 else if (can_turn_right)
7253 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7255 MovDir[x][y] = back_dir;
7257 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7259 else if (move_pattern == MV_HORIZONTAL ||
7260 move_pattern == MV_VERTICAL)
7262 if (move_pattern & old_move_dir)
7263 MovDir[x][y] = back_dir;
7264 else if (move_pattern == MV_HORIZONTAL)
7265 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7266 else if (move_pattern == MV_VERTICAL)
7267 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7269 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7271 else if (move_pattern & MV_ANY_DIRECTION)
7273 MovDir[x][y] = move_pattern;
7274 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7276 else if (move_pattern & MV_WIND_DIRECTION)
7278 MovDir[x][y] = game.wind_direction;
7279 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7281 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7283 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7284 MovDir[x][y] = left_dir;
7285 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7286 MovDir[x][y] = right_dir;
7288 if (MovDir[x][y] != old_move_dir)
7289 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7291 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7293 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7294 MovDir[x][y] = right_dir;
7295 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7296 MovDir[x][y] = left_dir;
7298 if (MovDir[x][y] != old_move_dir)
7299 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7301 else if (move_pattern == MV_TOWARDS_PLAYER ||
7302 move_pattern == MV_AWAY_FROM_PLAYER)
7304 int attr_x = -1, attr_y = -1;
7306 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7317 for (i = 0; i < MAX_PLAYERS; i++)
7319 struct PlayerInfo *player = &stored_player[i];
7320 int jx = player->jx, jy = player->jy;
7322 if (!player->active)
7326 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7334 MovDir[x][y] = MV_NONE;
7336 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7337 else if (attr_x > x)
7338 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7340 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7341 else if (attr_y > y)
7342 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7344 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7346 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7348 boolean first_horiz = RND(2);
7349 int new_move_dir = MovDir[x][y];
7351 if (element_info[element].move_stepsize == 0) /* "not moving" */
7353 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7354 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7360 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7361 Moving2Blocked(x, y, &newx, &newy);
7363 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7367 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368 Moving2Blocked(x, y, &newx, &newy);
7370 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7373 MovDir[x][y] = old_move_dir;
7376 else if (move_pattern == MV_WHEN_PUSHED ||
7377 move_pattern == MV_WHEN_DROPPED)
7379 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7380 MovDir[x][y] = MV_NONE;
7384 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7386 static int test_xy[7][2] =
7396 static int test_dir[7] =
7406 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7407 int move_preference = -1000000; /* start with very low preference */
7408 int new_move_dir = MV_NONE;
7409 int start_test = RND(4);
7412 for (i = 0; i < NUM_DIRECTIONS; i++)
7414 int move_dir = test_dir[start_test + i];
7415 int move_dir_preference;
7417 xx = x + test_xy[start_test + i][0];
7418 yy = y + test_xy[start_test + i][1];
7420 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7421 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7423 new_move_dir = move_dir;
7428 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7431 move_dir_preference = -1 * RunnerVisit[xx][yy];
7432 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7433 move_dir_preference = PlayerVisit[xx][yy];
7435 if (move_dir_preference > move_preference)
7437 /* prefer field that has not been visited for the longest time */
7438 move_preference = move_dir_preference;
7439 new_move_dir = move_dir;
7441 else if (move_dir_preference == move_preference &&
7442 move_dir == old_move_dir)
7444 /* prefer last direction when all directions are preferred equally */
7445 move_preference = move_dir_preference;
7446 new_move_dir = move_dir;
7450 MovDir[x][y] = new_move_dir;
7451 if (old_move_dir != new_move_dir)
7452 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7456 static void TurnRound(int x, int y)
7458 int direction = MovDir[x][y];
7462 GfxDir[x][y] = MovDir[x][y];
7464 if (direction != MovDir[x][y])
7468 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7470 ResetGfxFrame(x, y, FALSE);
7473 static boolean JustBeingPushed(int x, int y)
7477 for (i = 0; i < MAX_PLAYERS; i++)
7479 struct PlayerInfo *player = &stored_player[i];
7481 if (player->active && player->is_pushing && player->MovPos)
7483 int next_jx = player->jx + (player->jx - player->last_jx);
7484 int next_jy = player->jy + (player->jy - player->last_jy);
7486 if (x == next_jx && y == next_jy)
7494 void StartMoving(int x, int y)
7496 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7497 int element = Feld[x][y];
7502 if (MovDelay[x][y] == 0)
7503 GfxAction[x][y] = ACTION_DEFAULT;
7505 if (CAN_FALL(element) && y < lev_fieldy - 1)
7507 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7508 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7509 if (JustBeingPushed(x, y))
7512 if (element == EL_QUICKSAND_FULL)
7514 if (IS_FREE(x, y + 1))
7516 InitMovingField(x, y, MV_DOWN);
7517 started_moving = TRUE;
7519 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7520 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7521 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7522 Store[x][y] = EL_ROCK;
7524 Store[x][y] = EL_ROCK;
7527 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7529 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7531 if (!MovDelay[x][y])
7532 MovDelay[x][y] = TILEY + 1;
7541 Feld[x][y] = EL_QUICKSAND_EMPTY;
7542 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7543 Store[x][y + 1] = Store[x][y];
7546 PlayLevelSoundAction(x, y, ACTION_FILLING);
7549 else if (element == EL_QUICKSAND_FAST_FULL)
7551 if (IS_FREE(x, y + 1))
7553 InitMovingField(x, y, MV_DOWN);
7554 started_moving = TRUE;
7556 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7557 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7558 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7559 Store[x][y] = EL_ROCK;
7561 Store[x][y] = EL_ROCK;
7564 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7566 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7568 if (!MovDelay[x][y])
7569 MovDelay[x][y] = TILEY + 1;
7578 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7579 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7580 Store[x][y + 1] = Store[x][y];
7583 PlayLevelSoundAction(x, y, ACTION_FILLING);
7586 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7587 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7589 InitMovingField(x, y, MV_DOWN);
7590 started_moving = TRUE;
7592 Feld[x][y] = EL_QUICKSAND_FILLING;
7593 Store[x][y] = element;
7595 PlayLevelSoundAction(x, y, ACTION_FILLING);
7597 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7598 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7600 InitMovingField(x, y, MV_DOWN);
7601 started_moving = TRUE;
7603 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7604 Store[x][y] = element;
7606 PlayLevelSoundAction(x, y, ACTION_FILLING);
7608 else if (element == EL_MAGIC_WALL_FULL)
7610 if (IS_FREE(x, y + 1))
7612 InitMovingField(x, y, MV_DOWN);
7613 started_moving = TRUE;
7615 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7616 Store[x][y] = EL_CHANGED(Store[x][y]);
7618 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7620 if (!MovDelay[x][y])
7621 MovDelay[x][y] = TILEY/4 + 1;
7630 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7631 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7632 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7636 else if (element == EL_BD_MAGIC_WALL_FULL)
7638 if (IS_FREE(x, y + 1))
7640 InitMovingField(x, y, MV_DOWN);
7641 started_moving = TRUE;
7643 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7644 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7646 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7648 if (!MovDelay[x][y])
7649 MovDelay[x][y] = TILEY/4 + 1;
7658 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7659 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7660 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7664 else if (element == EL_DC_MAGIC_WALL_FULL)
7666 if (IS_FREE(x, y + 1))
7668 InitMovingField(x, y, MV_DOWN);
7669 started_moving = TRUE;
7671 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7672 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7674 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7676 if (!MovDelay[x][y])
7677 MovDelay[x][y] = TILEY/4 + 1;
7686 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7687 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7688 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7692 else if ((CAN_PASS_MAGIC_WALL(element) &&
7693 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7694 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7695 (CAN_PASS_DC_MAGIC_WALL(element) &&
7696 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7699 InitMovingField(x, y, MV_DOWN);
7700 started_moving = TRUE;
7703 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7704 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7705 EL_DC_MAGIC_WALL_FILLING);
7706 Store[x][y] = element;
7708 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7710 SplashAcid(x, y + 1);
7712 InitMovingField(x, y, MV_DOWN);
7713 started_moving = TRUE;
7715 Store[x][y] = EL_ACID;
7718 #if USE_FIX_IMPACT_COLLISION
7719 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7720 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7722 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7723 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7725 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7726 CAN_FALL(element) && WasJustFalling[x][y] &&
7727 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7729 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7730 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7731 (Feld[x][y + 1] == EL_BLOCKED)))
7733 /* this is needed for a special case not covered by calling "Impact()"
7734 from "ContinueMoving()": if an element moves to a tile directly below
7735 another element which was just falling on that tile (which was empty
7736 in the previous frame), the falling element above would just stop
7737 instead of smashing the element below (in previous version, the above
7738 element was just checked for "moving" instead of "falling", resulting
7739 in incorrect smashes caused by horizontal movement of the above
7740 element; also, the case of the player being the element to smash was
7741 simply not covered here... :-/ ) */
7743 CheckCollision[x][y] = 0;
7744 CheckImpact[x][y] = 0;
7748 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7750 if (MovDir[x][y] == MV_NONE)
7752 InitMovingField(x, y, MV_DOWN);
7753 started_moving = TRUE;
7756 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7758 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7759 MovDir[x][y] = MV_DOWN;
7761 InitMovingField(x, y, MV_DOWN);
7762 started_moving = TRUE;
7764 else if (element == EL_AMOEBA_DROP)
7766 Feld[x][y] = EL_AMOEBA_GROWING;
7767 Store[x][y] = EL_AMOEBA_WET;
7769 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7770 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7771 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7772 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7774 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
7775 (IS_FREE(x - 1, y + 1) ||
7776 Feld[x - 1][y + 1] == EL_ACID));
7777 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7778 (IS_FREE(x + 1, y + 1) ||
7779 Feld[x + 1][y + 1] == EL_ACID));
7780 boolean can_fall_any = (can_fall_left || can_fall_right);
7781 boolean can_fall_both = (can_fall_left && can_fall_right);
7782 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7784 #if USE_NEW_ALL_SLIPPERY
7785 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7787 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7788 can_fall_right = FALSE;
7789 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7790 can_fall_left = FALSE;
7791 else if (slippery_type == SLIPPERY_ONLY_LEFT)
7792 can_fall_right = FALSE;
7793 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7794 can_fall_left = FALSE;
7796 can_fall_any = (can_fall_left || can_fall_right);
7797 can_fall_both = FALSE;
7800 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7802 if (slippery_type == SLIPPERY_ONLY_LEFT)
7803 can_fall_right = FALSE;
7804 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7805 can_fall_left = FALSE;
7806 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7807 can_fall_right = FALSE;
7808 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7809 can_fall_left = FALSE;
7811 can_fall_any = (can_fall_left || can_fall_right);
7812 can_fall_both = (can_fall_left && can_fall_right);
7816 #if USE_NEW_ALL_SLIPPERY
7818 #if USE_NEW_SP_SLIPPERY
7819 /* !!! better use the same properties as for custom elements here !!! */
7820 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7821 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7823 can_fall_right = FALSE; /* slip down on left side */
7824 can_fall_both = FALSE;
7829 #if USE_NEW_ALL_SLIPPERY
7832 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7833 can_fall_right = FALSE; /* slip down on left side */
7835 can_fall_left = !(can_fall_right = RND(2));
7837 can_fall_both = FALSE;
7842 if (game.emulation == EMU_BOULDERDASH ||
7843 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7844 can_fall_right = FALSE; /* slip down on left side */
7846 can_fall_left = !(can_fall_right = RND(2));
7848 can_fall_both = FALSE;
7854 /* if not determined otherwise, prefer left side for slipping down */
7855 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7856 started_moving = TRUE;
7860 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7862 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7865 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
7866 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7867 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7868 int belt_dir = game.belt_dir[belt_nr];
7870 if ((belt_dir == MV_LEFT && left_is_free) ||
7871 (belt_dir == MV_RIGHT && right_is_free))
7873 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7875 InitMovingField(x, y, belt_dir);
7876 started_moving = TRUE;
7878 Pushed[x][y] = TRUE;
7879 Pushed[nextx][y] = TRUE;
7881 GfxAction[x][y] = ACTION_DEFAULT;
7885 MovDir[x][y] = 0; /* if element was moving, stop it */
7890 /* not "else if" because of elements that can fall and move (EL_SPRING) */
7892 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7894 if (CAN_MOVE(element) && !started_moving)
7897 int move_pattern = element_info[element].move_pattern;
7902 if (MovDir[x][y] == MV_NONE)
7904 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7905 x, y, element, element_info[element].token_name);
7906 printf("StartMoving(): This should never happen!\n");
7911 Moving2Blocked(x, y, &newx, &newy);
7913 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7916 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7917 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7919 WasJustMoving[x][y] = 0;
7920 CheckCollision[x][y] = 0;
7922 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7924 if (Feld[x][y] != element) /* element has changed */
7928 if (!MovDelay[x][y]) /* start new movement phase */
7930 /* all objects that can change their move direction after each step
7931 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7933 if (element != EL_YAMYAM &&
7934 element != EL_DARK_YAMYAM &&
7935 element != EL_PACMAN &&
7936 !(move_pattern & MV_ANY_DIRECTION) &&
7937 move_pattern != MV_TURNING_LEFT &&
7938 move_pattern != MV_TURNING_RIGHT &&
7939 move_pattern != MV_TURNING_LEFT_RIGHT &&
7940 move_pattern != MV_TURNING_RIGHT_LEFT &&
7941 move_pattern != MV_TURNING_RANDOM)
7945 if (MovDelay[x][y] && (element == EL_BUG ||
7946 element == EL_SPACESHIP ||
7947 element == EL_SP_SNIKSNAK ||
7948 element == EL_SP_ELECTRON ||
7949 element == EL_MOLE))
7950 DrawLevelField(x, y);
7954 if (MovDelay[x][y]) /* wait some time before next movement */
7958 if (element == EL_ROBOT ||
7959 element == EL_YAMYAM ||
7960 element == EL_DARK_YAMYAM)
7962 DrawLevelElementAnimationIfNeeded(x, y, element);
7963 PlayLevelSoundAction(x, y, ACTION_WAITING);
7965 else if (element == EL_SP_ELECTRON)
7966 DrawLevelElementAnimationIfNeeded(x, y, element);
7967 else if (element == EL_DRAGON)
7970 int dir = MovDir[x][y];
7971 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7972 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
7973 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
7974 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
7975 dir == MV_UP ? IMG_FLAMES_1_UP :
7976 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7977 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7979 GfxAction[x][y] = ACTION_ATTACKING;
7981 if (IS_PLAYER(x, y))
7982 DrawPlayerField(x, y);
7984 DrawLevelField(x, y);
7986 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7988 for (i = 1; i <= 3; i++)
7990 int xx = x + i * dx;
7991 int yy = y + i * dy;
7992 int sx = SCREENX(xx);
7993 int sy = SCREENY(yy);
7994 int flame_graphic = graphic + (i - 1);
7996 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8001 int flamed = MovingOrBlocked2Element(xx, yy);
8005 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8007 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8008 RemoveMovingField(xx, yy);
8010 RemoveField(xx, yy);
8012 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8015 RemoveMovingField(xx, yy);
8018 ChangeDelay[xx][yy] = 0;
8020 Feld[xx][yy] = EL_FLAMES;
8022 if (IN_SCR_FIELD(sx, sy))
8024 DrawLevelFieldCrumbledSand(xx, yy);
8025 DrawGraphic(sx, sy, flame_graphic, frame);
8030 if (Feld[xx][yy] == EL_FLAMES)
8031 Feld[xx][yy] = EL_EMPTY;
8032 DrawLevelField(xx, yy);
8037 if (MovDelay[x][y]) /* element still has to wait some time */
8039 PlayLevelSoundAction(x, y, ACTION_WAITING);
8045 /* now make next step */
8047 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8049 if (DONT_COLLIDE_WITH(element) &&
8050 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8051 !PLAYER_ENEMY_PROTECTED(newx, newy))
8053 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8058 else if (CAN_MOVE_INTO_ACID(element) &&
8059 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8060 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8061 (MovDir[x][y] == MV_DOWN ||
8062 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8064 SplashAcid(newx, newy);
8065 Store[x][y] = EL_ACID;
8067 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8069 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8070 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8071 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8072 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8075 DrawLevelField(x, y);
8077 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8078 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8079 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8081 local_player->friends_still_needed--;
8082 if (!local_player->friends_still_needed &&
8083 !local_player->GameOver && AllPlayersGone)
8084 PlayerWins(local_player);
8088 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8090 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8091 DrawLevelField(newx, newy);
8093 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8095 else if (!IS_FREE(newx, newy))
8097 GfxAction[x][y] = ACTION_WAITING;
8099 if (IS_PLAYER(x, y))
8100 DrawPlayerField(x, y);
8102 DrawLevelField(x, y);
8107 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8109 if (IS_FOOD_PIG(Feld[newx][newy]))
8111 if (IS_MOVING(newx, newy))
8112 RemoveMovingField(newx, newy);
8115 Feld[newx][newy] = EL_EMPTY;
8116 DrawLevelField(newx, newy);
8119 PlayLevelSound(x, y, SND_PIG_DIGGING);
8121 else if (!IS_FREE(newx, newy))
8123 if (IS_PLAYER(x, y))
8124 DrawPlayerField(x, y);
8126 DrawLevelField(x, y);
8131 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8133 if (Store[x][y] != EL_EMPTY)
8135 boolean can_clone = FALSE;
8138 /* check if element to clone is still there */
8139 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8141 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8149 /* cannot clone or target field not free anymore -- do not clone */
8150 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8151 Store[x][y] = EL_EMPTY;
8154 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8156 if (IS_MV_DIAGONAL(MovDir[x][y]))
8158 int diagonal_move_dir = MovDir[x][y];
8159 int stored = Store[x][y];
8160 int change_delay = 8;
8163 /* android is moving diagonally */
8165 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8167 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8168 GfxElement[x][y] = EL_EMC_ANDROID;
8169 GfxAction[x][y] = ACTION_SHRINKING;
8170 GfxDir[x][y] = diagonal_move_dir;
8171 ChangeDelay[x][y] = change_delay;
8173 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8176 DrawLevelGraphicAnimation(x, y, graphic);
8177 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8179 if (Feld[newx][newy] == EL_ACID)
8181 SplashAcid(newx, newy);
8186 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8188 Store[newx][newy] = EL_EMC_ANDROID;
8189 GfxElement[newx][newy] = EL_EMC_ANDROID;
8190 GfxAction[newx][newy] = ACTION_GROWING;
8191 GfxDir[newx][newy] = diagonal_move_dir;
8192 ChangeDelay[newx][newy] = change_delay;
8194 graphic = el_act_dir2img(GfxElement[newx][newy],
8195 GfxAction[newx][newy], GfxDir[newx][newy]);
8197 DrawLevelGraphicAnimation(newx, newy, graphic);
8198 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8204 Feld[newx][newy] = EL_EMPTY;
8205 DrawLevelField(newx, newy);
8207 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8210 else if (!IS_FREE(newx, newy))
8213 if (IS_PLAYER(x, y))
8214 DrawPlayerField(x, y);
8216 DrawLevelField(x, y);
8222 else if (IS_CUSTOM_ELEMENT(element) &&
8223 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8225 int new_element = Feld[newx][newy];
8227 if (!IS_FREE(newx, newy))
8229 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8230 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8233 /* no element can dig solid indestructible elements */
8234 if (IS_INDESTRUCTIBLE(new_element) &&
8235 !IS_DIGGABLE(new_element) &&
8236 !IS_COLLECTIBLE(new_element))
8239 if (AmoebaNr[newx][newy] &&
8240 (new_element == EL_AMOEBA_FULL ||
8241 new_element == EL_BD_AMOEBA ||
8242 new_element == EL_AMOEBA_GROWING))
8244 AmoebaCnt[AmoebaNr[newx][newy]]--;
8245 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8248 if (IS_MOVING(newx, newy))
8249 RemoveMovingField(newx, newy);
8252 RemoveField(newx, newy);
8253 DrawLevelField(newx, newy);
8256 /* if digged element was about to explode, prevent the explosion */
8257 ExplodeField[newx][newy] = EX_TYPE_NONE;
8259 PlayLevelSoundAction(x, y, action);
8262 Store[newx][newy] = EL_EMPTY;
8264 /* this makes it possible to leave the removed element again */
8265 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8266 Store[newx][newy] = new_element;
8268 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8270 int move_leave_element = element_info[element].move_leave_element;
8272 /* this makes it possible to leave the removed element again */
8273 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8274 new_element : move_leave_element);
8278 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8280 RunnerVisit[x][y] = FrameCounter;
8281 PlayerVisit[x][y] /= 8; /* expire player visit path */
8284 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8286 if (!IS_FREE(newx, newy))
8288 if (IS_PLAYER(x, y))
8289 DrawPlayerField(x, y);
8291 DrawLevelField(x, y);
8297 boolean wanna_flame = !RND(10);
8298 int dx = newx - x, dy = newy - y;
8299 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8300 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8301 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8302 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8303 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8304 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8307 IS_CLASSIC_ENEMY(element1) ||
8308 IS_CLASSIC_ENEMY(element2)) &&
8309 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8310 element1 != EL_FLAMES && element2 != EL_FLAMES)
8312 ResetGfxAnimation(x, y);
8313 GfxAction[x][y] = ACTION_ATTACKING;
8315 if (IS_PLAYER(x, y))
8316 DrawPlayerField(x, y);
8318 DrawLevelField(x, y);
8320 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8322 MovDelay[x][y] = 50;
8326 RemoveField(newx, newy);
8328 Feld[newx][newy] = EL_FLAMES;
8329 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8332 RemoveField(newx1, newy1);
8334 Feld[newx1][newy1] = EL_FLAMES;
8336 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8339 RemoveField(newx2, newy2);
8341 Feld[newx2][newy2] = EL_FLAMES;
8348 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8349 Feld[newx][newy] == EL_DIAMOND)
8351 if (IS_MOVING(newx, newy))
8352 RemoveMovingField(newx, newy);
8355 Feld[newx][newy] = EL_EMPTY;
8356 DrawLevelField(newx, newy);
8359 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8361 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8362 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8364 if (AmoebaNr[newx][newy])
8366 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8367 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8368 Feld[newx][newy] == EL_BD_AMOEBA)
8369 AmoebaCnt[AmoebaNr[newx][newy]]--;
8374 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8376 RemoveMovingField(newx, newy);
8379 if (IS_MOVING(newx, newy))
8381 RemoveMovingField(newx, newy);
8386 Feld[newx][newy] = EL_EMPTY;
8387 DrawLevelField(newx, newy);
8390 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8392 else if ((element == EL_PACMAN || element == EL_MOLE)
8393 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8395 if (AmoebaNr[newx][newy])
8397 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8398 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8399 Feld[newx][newy] == EL_BD_AMOEBA)
8400 AmoebaCnt[AmoebaNr[newx][newy]]--;
8403 if (element == EL_MOLE)
8405 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8406 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8408 ResetGfxAnimation(x, y);
8409 GfxAction[x][y] = ACTION_DIGGING;
8410 DrawLevelField(x, y);
8412 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8414 return; /* wait for shrinking amoeba */
8416 else /* element == EL_PACMAN */
8418 Feld[newx][newy] = EL_EMPTY;
8419 DrawLevelField(newx, newy);
8420 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8423 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8424 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8425 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8427 /* wait for shrinking amoeba to completely disappear */
8430 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8432 /* object was running against a wall */
8437 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8438 if (move_pattern & MV_ANY_DIRECTION &&
8439 move_pattern == MovDir[x][y])
8441 int blocking_element =
8442 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8444 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8447 element = Feld[x][y]; /* element might have changed */
8451 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8452 DrawLevelElementAnimation(x, y, element);
8454 if (DONT_TOUCH(element))
8455 TestIfBadThingTouchesPlayer(x, y);
8460 InitMovingField(x, y, MovDir[x][y]);
8462 PlayLevelSoundAction(x, y, ACTION_MOVING);
8466 ContinueMoving(x, y);
8469 void ContinueMoving(int x, int y)
8471 int element = Feld[x][y];
8472 struct ElementInfo *ei = &element_info[element];
8473 int direction = MovDir[x][y];
8474 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8475 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8476 int newx = x + dx, newy = y + dy;
8477 int stored = Store[x][y];
8478 int stored_new = Store[newx][newy];
8479 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8480 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8481 boolean last_line = (newy == lev_fieldy - 1);
8483 MovPos[x][y] += getElementMoveStepsize(x, y);
8485 if (pushed_by_player) /* special case: moving object pushed by player */
8486 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8488 if (ABS(MovPos[x][y]) < TILEX)
8491 int ee = Feld[x][y];
8492 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8493 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8495 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8496 x, y, ABS(MovPos[x][y]),
8498 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8501 DrawLevelField(x, y);
8503 return; /* element is still moving */
8506 /* element reached destination field */
8508 Feld[x][y] = EL_EMPTY;
8509 Feld[newx][newy] = element;
8510 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8512 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8514 element = Feld[newx][newy] = EL_ACID;
8516 else if (element == EL_MOLE)
8518 Feld[x][y] = EL_SAND;
8520 DrawLevelFieldCrumbledSandNeighbours(x, y);
8522 else if (element == EL_QUICKSAND_FILLING)
8524 element = Feld[newx][newy] = get_next_element(element);
8525 Store[newx][newy] = Store[x][y];
8527 else if (element == EL_QUICKSAND_EMPTYING)
8529 Feld[x][y] = get_next_element(element);
8530 element = Feld[newx][newy] = Store[x][y];
8532 else if (element == EL_QUICKSAND_FAST_FILLING)
8534 element = Feld[newx][newy] = get_next_element(element);
8535 Store[newx][newy] = Store[x][y];
8537 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8539 Feld[x][y] = get_next_element(element);
8540 element = Feld[newx][newy] = Store[x][y];
8542 else if (element == EL_MAGIC_WALL_FILLING)
8544 element = Feld[newx][newy] = get_next_element(element);
8545 if (!game.magic_wall_active)
8546 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8547 Store[newx][newy] = Store[x][y];
8549 else if (element == EL_MAGIC_WALL_EMPTYING)
8551 Feld[x][y] = get_next_element(element);
8552 if (!game.magic_wall_active)
8553 Feld[x][y] = EL_MAGIC_WALL_DEAD;
8554 element = Feld[newx][newy] = Store[x][y];
8556 #if USE_NEW_CUSTOM_VALUE
8557 InitField(newx, newy, FALSE);
8560 else if (element == EL_BD_MAGIC_WALL_FILLING)
8562 element = Feld[newx][newy] = get_next_element(element);
8563 if (!game.magic_wall_active)
8564 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8565 Store[newx][newy] = Store[x][y];
8567 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8569 Feld[x][y] = get_next_element(element);
8570 if (!game.magic_wall_active)
8571 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8572 element = Feld[newx][newy] = Store[x][y];
8574 #if USE_NEW_CUSTOM_VALUE
8575 InitField(newx, newy, FALSE);
8578 else if (element == EL_DC_MAGIC_WALL_FILLING)
8580 element = Feld[newx][newy] = get_next_element(element);
8581 if (!game.magic_wall_active)
8582 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8583 Store[newx][newy] = Store[x][y];
8585 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8587 Feld[x][y] = get_next_element(element);
8588 if (!game.magic_wall_active)
8589 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8590 element = Feld[newx][newy] = Store[x][y];
8592 #if USE_NEW_CUSTOM_VALUE
8593 InitField(newx, newy, FALSE);
8596 else if (element == EL_AMOEBA_DROPPING)
8598 Feld[x][y] = get_next_element(element);
8599 element = Feld[newx][newy] = Store[x][y];
8601 else if (element == EL_SOKOBAN_OBJECT)
8604 Feld[x][y] = Back[x][y];
8606 if (Back[newx][newy])
8607 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8609 Back[x][y] = Back[newx][newy] = 0;
8612 Store[x][y] = EL_EMPTY;
8617 MovDelay[newx][newy] = 0;
8619 if (CAN_CHANGE_OR_HAS_ACTION(element))
8621 /* copy element change control values to new field */
8622 ChangeDelay[newx][newy] = ChangeDelay[x][y];
8623 ChangePage[newx][newy] = ChangePage[x][y];
8624 ChangeCount[newx][newy] = ChangeCount[x][y];
8625 ChangeEvent[newx][newy] = ChangeEvent[x][y];
8628 #if USE_NEW_CUSTOM_VALUE
8629 CustomValue[newx][newy] = CustomValue[x][y];
8632 ChangeDelay[x][y] = 0;
8633 ChangePage[x][y] = -1;
8634 ChangeCount[x][y] = 0;
8635 ChangeEvent[x][y] = -1;
8637 #if USE_NEW_CUSTOM_VALUE
8638 CustomValue[x][y] = 0;
8641 /* copy animation control values to new field */
8642 GfxFrame[newx][newy] = GfxFrame[x][y];
8643 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
8644 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
8645 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
8647 Pushed[x][y] = Pushed[newx][newy] = FALSE;
8649 /* some elements can leave other elements behind after moving */
8651 if (ei->move_leave_element != EL_EMPTY &&
8652 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8653 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8655 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8656 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8657 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8660 int move_leave_element = ei->move_leave_element;
8664 /* this makes it possible to leave the removed element again */
8665 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8666 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8668 /* this makes it possible to leave the removed element again */
8669 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8670 move_leave_element = stored;
8673 /* this makes it possible to leave the removed element again */
8674 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8675 ei->move_leave_element == EL_TRIGGER_ELEMENT)
8676 move_leave_element = stored;
8679 Feld[x][y] = move_leave_element;
8681 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8682 MovDir[x][y] = direction;
8684 InitField(x, y, FALSE);
8686 if (GFX_CRUMBLED(Feld[x][y]))
8687 DrawLevelFieldCrumbledSandNeighbours(x, y);
8689 if (ELEM_IS_PLAYER(move_leave_element))
8690 RelocatePlayer(x, y, move_leave_element);
8693 /* do this after checking for left-behind element */
8694 ResetGfxAnimation(x, y); /* reset animation values for old field */
8696 if (!CAN_MOVE(element) ||
8697 (CAN_FALL(element) && direction == MV_DOWN &&
8698 (element == EL_SPRING ||
8699 element_info[element].move_pattern == MV_WHEN_PUSHED ||
8700 element_info[element].move_pattern == MV_WHEN_DROPPED)))
8701 GfxDir[x][y] = MovDir[newx][newy] = 0;
8703 DrawLevelField(x, y);
8704 DrawLevelField(newx, newy);
8706 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
8708 /* prevent pushed element from moving on in pushed direction */
8709 if (pushed_by_player && CAN_MOVE(element) &&
8710 element_info[element].move_pattern & MV_ANY_DIRECTION &&
8711 !(element_info[element].move_pattern & direction))
8712 TurnRound(newx, newy);
8714 /* prevent elements on conveyor belt from moving on in last direction */
8715 if (pushed_by_conveyor && CAN_FALL(element) &&
8716 direction & MV_HORIZONTAL)
8717 MovDir[newx][newy] = 0;
8719 if (!pushed_by_player)
8721 int nextx = newx + dx, nexty = newy + dy;
8722 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8724 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8726 if (CAN_FALL(element) && direction == MV_DOWN)
8727 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8729 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8730 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8732 #if USE_FIX_IMPACT_COLLISION
8733 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8734 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8738 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
8740 TestIfBadThingTouchesPlayer(newx, newy);
8741 TestIfBadThingTouchesFriend(newx, newy);
8743 if (!IS_CUSTOM_ELEMENT(element))
8744 TestIfBadThingTouchesOtherBadThing(newx, newy);
8746 else if (element == EL_PENGUIN)
8747 TestIfFriendTouchesBadThing(newx, newy);
8749 /* give the player one last chance (one more frame) to move away */
8750 if (CAN_FALL(element) && direction == MV_DOWN &&
8751 (last_line || (!IS_FREE(x, newy + 1) &&
8752 (!IS_PLAYER(x, newy + 1) ||
8753 game.engine_version < VERSION_IDENT(3,1,1,0)))))
8756 if (pushed_by_player && !game.use_change_when_pushing_bug)
8758 int push_side = MV_DIR_OPPOSITE(direction);
8759 struct PlayerInfo *player = PLAYERINFO(x, y);
8761 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8762 player->index_bit, push_side);
8763 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8764 player->index_bit, push_side);
8767 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
8768 MovDelay[newx][newy] = 1;
8770 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8772 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
8775 if (ChangePage[newx][newy] != -1) /* delayed change */
8777 int page = ChangePage[newx][newy];
8778 struct ElementChangeInfo *change = &ei->change_page[page];
8780 ChangePage[newx][newy] = -1;
8782 if (change->can_change)
8784 if (ChangeElement(newx, newy, element, page))
8786 if (change->post_change_function)
8787 change->post_change_function(newx, newy);
8791 if (change->has_action)
8792 ExecuteCustomElementAction(newx, newy, element, page);
8796 TestIfElementHitsCustomElement(newx, newy, direction);
8797 TestIfPlayerTouchesCustomElement(newx, newy);
8798 TestIfElementTouchesCustomElement(newx, newy);
8800 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8801 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8802 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8803 MV_DIR_OPPOSITE(direction));
8806 int AmoebeNachbarNr(int ax, int ay)
8809 int element = Feld[ax][ay];
8811 static int xy[4][2] =
8819 for (i = 0; i < NUM_DIRECTIONS; i++)
8821 int x = ax + xy[i][0];
8822 int y = ay + xy[i][1];
8824 if (!IN_LEV_FIELD(x, y))
8827 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8828 group_nr = AmoebaNr[x][y];
8834 void AmoebenVereinigen(int ax, int ay)
8836 int i, x, y, xx, yy;
8837 int new_group_nr = AmoebaNr[ax][ay];
8838 static int xy[4][2] =
8846 if (new_group_nr == 0)
8849 for (i = 0; i < NUM_DIRECTIONS; i++)
8854 if (!IN_LEV_FIELD(x, y))
8857 if ((Feld[x][y] == EL_AMOEBA_FULL ||
8858 Feld[x][y] == EL_BD_AMOEBA ||
8859 Feld[x][y] == EL_AMOEBA_DEAD) &&
8860 AmoebaNr[x][y] != new_group_nr)
8862 int old_group_nr = AmoebaNr[x][y];
8864 if (old_group_nr == 0)
8867 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8868 AmoebaCnt[old_group_nr] = 0;
8869 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8870 AmoebaCnt2[old_group_nr] = 0;
8872 SCAN_PLAYFIELD(xx, yy)
8874 if (AmoebaNr[xx][yy] == old_group_nr)
8875 AmoebaNr[xx][yy] = new_group_nr;
8881 void AmoebeUmwandeln(int ax, int ay)
8885 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8887 int group_nr = AmoebaNr[ax][ay];
8892 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8893 printf("AmoebeUmwandeln(): This should never happen!\n");
8898 SCAN_PLAYFIELD(x, y)
8900 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8903 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8907 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8908 SND_AMOEBA_TURNING_TO_GEM :
8909 SND_AMOEBA_TURNING_TO_ROCK));
8914 static int xy[4][2] =
8922 for (i = 0; i < NUM_DIRECTIONS; i++)
8927 if (!IN_LEV_FIELD(x, y))
8930 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8932 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8933 SND_AMOEBA_TURNING_TO_GEM :
8934 SND_AMOEBA_TURNING_TO_ROCK));
8941 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8944 int group_nr = AmoebaNr[ax][ay];
8945 boolean done = FALSE;
8950 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8951 printf("AmoebeUmwandelnBD(): This should never happen!\n");
8956 SCAN_PLAYFIELD(x, y)
8958 if (AmoebaNr[x][y] == group_nr &&
8959 (Feld[x][y] == EL_AMOEBA_DEAD ||
8960 Feld[x][y] == EL_BD_AMOEBA ||
8961 Feld[x][y] == EL_AMOEBA_GROWING))
8964 Feld[x][y] = new_element;
8965 InitField(x, y, FALSE);
8966 DrawLevelField(x, y);
8972 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8973 SND_BD_AMOEBA_TURNING_TO_ROCK :
8974 SND_BD_AMOEBA_TURNING_TO_GEM));
8977 void AmoebeWaechst(int x, int y)
8979 static unsigned long sound_delay = 0;
8980 static unsigned long sound_delay_value = 0;
8982 if (!MovDelay[x][y]) /* start new growing cycle */
8986 if (DelayReached(&sound_delay, sound_delay_value))
8988 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8989 sound_delay_value = 30;
8993 if (MovDelay[x][y]) /* wait some time before growing bigger */
8996 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8998 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8999 6 - MovDelay[x][y]);
9001 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9004 if (!MovDelay[x][y])
9006 Feld[x][y] = Store[x][y];
9008 DrawLevelField(x, y);
9013 void AmoebaDisappearing(int x, int y)
9015 static unsigned long sound_delay = 0;
9016 static unsigned long sound_delay_value = 0;
9018 if (!MovDelay[x][y]) /* start new shrinking cycle */
9022 if (DelayReached(&sound_delay, sound_delay_value))
9023 sound_delay_value = 30;
9026 if (MovDelay[x][y]) /* wait some time before shrinking */
9029 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9031 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9032 6 - MovDelay[x][y]);
9034 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9037 if (!MovDelay[x][y])
9039 Feld[x][y] = EL_EMPTY;
9040 DrawLevelField(x, y);
9042 /* don't let mole enter this field in this cycle;
9043 (give priority to objects falling to this field from above) */
9049 void AmoebeAbleger(int ax, int ay)
9052 int element = Feld[ax][ay];
9053 int graphic = el2img(element);
9054 int newax = ax, neway = ay;
9055 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9056 static int xy[4][2] =
9064 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9066 Feld[ax][ay] = EL_AMOEBA_DEAD;
9067 DrawLevelField(ax, ay);
9071 if (IS_ANIMATED(graphic))
9072 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9074 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9075 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9077 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9080 if (MovDelay[ax][ay])
9084 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9087 int x = ax + xy[start][0];
9088 int y = ay + xy[start][1];
9090 if (!IN_LEV_FIELD(x, y))
9093 if (IS_FREE(x, y) ||
9094 CAN_GROW_INTO(Feld[x][y]) ||
9095 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9096 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9102 if (newax == ax && neway == ay)
9105 else /* normal or "filled" (BD style) amoeba */
9108 boolean waiting_for_player = FALSE;
9110 for (i = 0; i < NUM_DIRECTIONS; i++)
9112 int j = (start + i) % 4;
9113 int x = ax + xy[j][0];
9114 int y = ay + xy[j][1];
9116 if (!IN_LEV_FIELD(x, y))
9119 if (IS_FREE(x, y) ||
9120 CAN_GROW_INTO(Feld[x][y]) ||
9121 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9122 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9128 else if (IS_PLAYER(x, y))
9129 waiting_for_player = TRUE;
9132 if (newax == ax && neway == ay) /* amoeba cannot grow */
9134 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9136 Feld[ax][ay] = EL_AMOEBA_DEAD;
9137 DrawLevelField(ax, ay);
9138 AmoebaCnt[AmoebaNr[ax][ay]]--;
9140 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9142 if (element == EL_AMOEBA_FULL)
9143 AmoebeUmwandeln(ax, ay);
9144 else if (element == EL_BD_AMOEBA)
9145 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9150 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9152 /* amoeba gets larger by growing in some direction */
9154 int new_group_nr = AmoebaNr[ax][ay];
9157 if (new_group_nr == 0)
9159 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9160 printf("AmoebeAbleger(): This should never happen!\n");
9165 AmoebaNr[newax][neway] = new_group_nr;
9166 AmoebaCnt[new_group_nr]++;
9167 AmoebaCnt2[new_group_nr]++;
9169 /* if amoeba touches other amoeba(s) after growing, unify them */
9170 AmoebenVereinigen(newax, neway);
9172 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9174 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9180 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9181 (neway == lev_fieldy - 1 && newax != ax))
9183 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9184 Store[newax][neway] = element;
9186 else if (neway == ay || element == EL_EMC_DRIPPER)
9188 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9190 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9194 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9195 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9196 Store[ax][ay] = EL_AMOEBA_DROP;
9197 ContinueMoving(ax, ay);
9201 DrawLevelField(newax, neway);
9204 void Life(int ax, int ay)
9208 int element = Feld[ax][ay];
9209 int graphic = el2img(element);
9210 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9212 boolean changed = FALSE;
9214 if (IS_ANIMATED(graphic))
9215 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9220 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9221 MovDelay[ax][ay] = life_time;
9223 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9226 if (MovDelay[ax][ay])
9230 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9232 int xx = ax+x1, yy = ay+y1;
9235 if (!IN_LEV_FIELD(xx, yy))
9238 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9240 int x = xx+x2, y = yy+y2;
9242 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9245 if (((Feld[x][y] == element ||
9246 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9248 (IS_FREE(x, y) && Stop[x][y]))
9252 if (xx == ax && yy == ay) /* field in the middle */
9254 if (nachbarn < life_parameter[0] ||
9255 nachbarn > life_parameter[1])
9257 Feld[xx][yy] = EL_EMPTY;
9259 DrawLevelField(xx, yy);
9260 Stop[xx][yy] = TRUE;
9264 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9265 { /* free border field */
9266 if (nachbarn >= life_parameter[2] &&
9267 nachbarn <= life_parameter[3])
9269 Feld[xx][yy] = element;
9270 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9272 DrawLevelField(xx, yy);
9273 Stop[xx][yy] = TRUE;
9280 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9281 SND_GAME_OF_LIFE_GROWING);
9284 static void InitRobotWheel(int x, int y)
9286 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9289 static void RunRobotWheel(int x, int y)
9291 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9294 static void StopRobotWheel(int x, int y)
9296 if (ZX == x && ZY == y)
9300 game.robot_wheel_active = FALSE;
9304 static void InitTimegateWheel(int x, int y)
9306 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9309 static void RunTimegateWheel(int x, int y)
9311 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9314 static void InitMagicBallDelay(int x, int y)
9317 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9319 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9323 static void ActivateMagicBall(int bx, int by)
9327 if (level.ball_random)
9329 int pos_border = RND(8); /* select one of the eight border elements */
9330 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9331 int xx = pos_content % 3;
9332 int yy = pos_content / 3;
9337 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9338 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9342 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9344 int xx = x - bx + 1;
9345 int yy = y - by + 1;
9347 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9348 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9352 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9355 void CheckExit(int x, int y)
9357 if (local_player->gems_still_needed > 0 ||
9358 local_player->sokobanfields_still_needed > 0 ||
9359 local_player->lights_still_needed > 0)
9361 int element = Feld[x][y];
9362 int graphic = el2img(element);
9364 if (IS_ANIMATED(graphic))
9365 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9370 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9373 Feld[x][y] = EL_EXIT_OPENING;
9375 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9378 void CheckExitEM(int x, int y)
9380 if (local_player->gems_still_needed > 0 ||
9381 local_player->sokobanfields_still_needed > 0 ||
9382 local_player->lights_still_needed > 0)
9384 int element = Feld[x][y];
9385 int graphic = el2img(element);
9387 if (IS_ANIMATED(graphic))
9388 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9393 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9396 Feld[x][y] = EL_EM_EXIT_OPENING;
9398 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9401 void CheckExitSteel(int x, int y)
9403 if (local_player->gems_still_needed > 0 ||
9404 local_player->sokobanfields_still_needed > 0 ||
9405 local_player->lights_still_needed > 0)
9407 int element = Feld[x][y];
9408 int graphic = el2img(element);
9410 if (IS_ANIMATED(graphic))
9411 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9416 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9419 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9421 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9424 void CheckExitSteelEM(int x, int y)
9426 if (local_player->gems_still_needed > 0 ||
9427 local_player->sokobanfields_still_needed > 0 ||
9428 local_player->lights_still_needed > 0)
9430 int element = Feld[x][y];
9431 int graphic = el2img(element);
9433 if (IS_ANIMATED(graphic))
9434 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9439 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9442 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9444 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9447 void CheckExitSP(int x, int y)
9449 if (local_player->gems_still_needed > 0)
9451 int element = Feld[x][y];
9452 int graphic = el2img(element);
9454 if (IS_ANIMATED(graphic))
9455 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9460 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9463 Feld[x][y] = EL_SP_EXIT_OPENING;
9465 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9468 static void CloseAllOpenTimegates()
9472 SCAN_PLAYFIELD(x, y)
9474 int element = Feld[x][y];
9476 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9478 Feld[x][y] = EL_TIMEGATE_CLOSING;
9480 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9485 void DrawTwinkleOnField(int x, int y)
9487 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9490 if (Feld[x][y] == EL_BD_DIAMOND)
9493 if (MovDelay[x][y] == 0) /* next animation frame */
9494 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9496 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9500 if (setup.direct_draw && MovDelay[x][y])
9501 SetDrawtoField(DRAW_BUFFERED);
9503 DrawLevelElementAnimation(x, y, Feld[x][y]);
9505 if (MovDelay[x][y] != 0)
9507 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9508 10 - MovDelay[x][y]);
9510 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9512 if (setup.direct_draw)
9516 dest_x = FX + SCREENX(x) * TILEX;
9517 dest_y = FY + SCREENY(y) * TILEY;
9519 BlitBitmap(drawto_field, window,
9520 dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9521 SetDrawtoField(DRAW_DIRECT);
9527 void MauerWaechst(int x, int y)
9531 if (!MovDelay[x][y]) /* next animation frame */
9532 MovDelay[x][y] = 3 * delay;
9534 if (MovDelay[x][y]) /* wait some time before next frame */
9538 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9540 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9541 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9543 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9546 if (!MovDelay[x][y])
9548 if (MovDir[x][y] == MV_LEFT)
9550 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9551 DrawLevelField(x - 1, y);
9553 else if (MovDir[x][y] == MV_RIGHT)
9555 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9556 DrawLevelField(x + 1, y);
9558 else if (MovDir[x][y] == MV_UP)
9560 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9561 DrawLevelField(x, y - 1);
9565 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9566 DrawLevelField(x, y + 1);
9569 Feld[x][y] = Store[x][y];
9571 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9572 DrawLevelField(x, y);
9577 void MauerAbleger(int ax, int ay)
9579 int element = Feld[ax][ay];
9580 int graphic = el2img(element);
9581 boolean oben_frei = FALSE, unten_frei = FALSE;
9582 boolean links_frei = FALSE, rechts_frei = FALSE;
9583 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9584 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9585 boolean new_wall = FALSE;
9587 if (IS_ANIMATED(graphic))
9588 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9590 if (!MovDelay[ax][ay]) /* start building new wall */
9591 MovDelay[ax][ay] = 6;
9593 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9596 if (MovDelay[ax][ay])
9600 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9602 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9604 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9606 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9609 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9610 element == EL_EXPANDABLE_WALL_ANY)
9614 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9615 Store[ax][ay-1] = element;
9616 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9617 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9618 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9619 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9624 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9625 Store[ax][ay+1] = element;
9626 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9627 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9628 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9629 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9634 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9635 element == EL_EXPANDABLE_WALL_ANY ||
9636 element == EL_EXPANDABLE_WALL ||
9637 element == EL_BD_EXPANDABLE_WALL)
9641 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9642 Store[ax-1][ay] = element;
9643 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9644 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9645 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9646 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9652 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9653 Store[ax+1][ay] = element;
9654 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9655 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9656 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9657 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9662 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9663 DrawLevelField(ax, ay);
9665 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9667 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9668 unten_massiv = TRUE;
9669 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9670 links_massiv = TRUE;
9671 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9672 rechts_massiv = TRUE;
9674 if (((oben_massiv && unten_massiv) ||
9675 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9676 element == EL_EXPANDABLE_WALL) &&
9677 ((links_massiv && rechts_massiv) ||
9678 element == EL_EXPANDABLE_WALL_VERTICAL))
9679 Feld[ax][ay] = EL_WALL;
9682 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9685 void MauerAblegerStahl(int ax, int ay)
9687 int element = Feld[ax][ay];
9688 int graphic = el2img(element);
9689 boolean oben_frei = FALSE, unten_frei = FALSE;
9690 boolean links_frei = FALSE, rechts_frei = FALSE;
9691 boolean oben_massiv = FALSE, unten_massiv = FALSE;
9692 boolean links_massiv = FALSE, rechts_massiv = FALSE;
9693 boolean new_wall = FALSE;
9695 if (IS_ANIMATED(graphic))
9696 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9698 if (!MovDelay[ax][ay]) /* start building new wall */
9699 MovDelay[ax][ay] = 6;
9701 if (MovDelay[ax][ay]) /* wait some time before building new wall */
9704 if (MovDelay[ax][ay])
9708 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9710 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9712 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9714 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9717 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9718 element == EL_EXPANDABLE_STEELWALL_ANY)
9722 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9723 Store[ax][ay-1] = element;
9724 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9725 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9726 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9727 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9732 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9733 Store[ax][ay+1] = element;
9734 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9735 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9736 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9737 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9742 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9743 element == EL_EXPANDABLE_STEELWALL_ANY)
9747 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9748 Store[ax-1][ay] = element;
9749 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9750 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9751 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9752 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9758 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9759 Store[ax+1][ay] = element;
9760 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9761 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9762 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9763 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9768 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9770 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9771 unten_massiv = TRUE;
9772 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9773 links_massiv = TRUE;
9774 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9775 rechts_massiv = TRUE;
9777 if (((oben_massiv && unten_massiv) ||
9778 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9779 ((links_massiv && rechts_massiv) ||
9780 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9781 Feld[ax][ay] = EL_WALL;
9784 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9787 void CheckForDragon(int x, int y)
9790 boolean dragon_found = FALSE;
9791 static int xy[4][2] =
9799 for (i = 0; i < NUM_DIRECTIONS; i++)
9801 for (j = 0; j < 4; j++)
9803 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9805 if (IN_LEV_FIELD(xx, yy) &&
9806 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9808 if (Feld[xx][yy] == EL_DRAGON)
9809 dragon_found = TRUE;
9818 for (i = 0; i < NUM_DIRECTIONS; i++)
9820 for (j = 0; j < 3; j++)
9822 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9824 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9826 Feld[xx][yy] = EL_EMPTY;
9827 DrawLevelField(xx, yy);
9836 static void InitBuggyBase(int x, int y)
9838 int element = Feld[x][y];
9839 int activating_delay = FRAMES_PER_SECOND / 4;
9842 (element == EL_SP_BUGGY_BASE ?
9843 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9844 element == EL_SP_BUGGY_BASE_ACTIVATING ?
9846 element == EL_SP_BUGGY_BASE_ACTIVE ?
9847 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9850 static void WarnBuggyBase(int x, int y)
9853 static int xy[4][2] =
9861 for (i = 0; i < NUM_DIRECTIONS; i++)
9863 int xx = x + xy[i][0];
9864 int yy = y + xy[i][1];
9866 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9868 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9875 static void InitTrap(int x, int y)
9877 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9880 static void ActivateTrap(int x, int y)
9882 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9885 static void ChangeActiveTrap(int x, int y)
9887 int graphic = IMG_TRAP_ACTIVE;
9889 /* if new animation frame was drawn, correct crumbled sand border */
9890 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9891 DrawLevelFieldCrumbledSand(x, y);
9894 static int getSpecialActionElement(int element, int number, int base_element)
9896 return (element != EL_EMPTY ? element :
9897 number != -1 ? base_element + number - 1 :
9901 static int getModifiedActionNumber(int value_old, int operator, int operand,
9902 int value_min, int value_max)
9904 int value_new = (operator == CA_MODE_SET ? operand :
9905 operator == CA_MODE_ADD ? value_old + operand :
9906 operator == CA_MODE_SUBTRACT ? value_old - operand :
9907 operator == CA_MODE_MULTIPLY ? value_old * operand :
9908 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
9909 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
9912 return (value_new < value_min ? value_min :
9913 value_new > value_max ? value_max :
9917 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9919 struct ElementInfo *ei = &element_info[element];
9920 struct ElementChangeInfo *change = &ei->change_page[page];
9921 int target_element = change->target_element;
9922 int action_type = change->action_type;
9923 int action_mode = change->action_mode;
9924 int action_arg = change->action_arg;
9927 if (!change->has_action)
9930 /* ---------- determine action paramater values -------------------------- */
9932 int level_time_value =
9933 (level.time > 0 ? TimeLeft :
9936 int action_arg_element =
9937 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
9938 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9939 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
9942 int action_arg_direction =
9943 (action_arg >= CA_ARG_DIRECTION_LEFT &&
9944 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9945 action_arg == CA_ARG_DIRECTION_TRIGGER ?
9946 change->actual_trigger_side :
9947 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9948 MV_DIR_OPPOSITE(change->actual_trigger_side) :
9951 int action_arg_number_min =
9952 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9955 int action_arg_number_max =
9956 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9957 action_type == CA_SET_LEVEL_GEMS ? 999 :
9958 action_type == CA_SET_LEVEL_TIME ? 9999 :
9959 action_type == CA_SET_LEVEL_SCORE ? 99999 :
9960 action_type == CA_SET_CE_VALUE ? 9999 :
9961 action_type == CA_SET_CE_SCORE ? 9999 :
9964 int action_arg_number_reset =
9965 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9966 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9967 action_type == CA_SET_LEVEL_TIME ? level.time :
9968 action_type == CA_SET_LEVEL_SCORE ? 0 :
9969 #if USE_NEW_CUSTOM_VALUE
9970 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9972 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9974 action_type == CA_SET_CE_SCORE ? 0 :
9977 int action_arg_number =
9978 (action_arg <= CA_ARG_MAX ? action_arg :
9979 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9980 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9981 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9982 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9983 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9984 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9985 #if USE_NEW_CUSTOM_VALUE
9986 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9988 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9990 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9991 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9992 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9993 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9994 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9995 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9996 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9997 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9998 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9999 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10000 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10003 int action_arg_number_old =
10004 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10005 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10006 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10007 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10008 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10011 int action_arg_number_new =
10012 getModifiedActionNumber(action_arg_number_old,
10013 action_mode, action_arg_number,
10014 action_arg_number_min, action_arg_number_max);
10016 int trigger_player_bits =
10017 (change->actual_trigger_player >= EL_PLAYER_1 &&
10018 change->actual_trigger_player <= EL_PLAYER_4 ?
10019 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10022 int action_arg_player_bits =
10023 (action_arg >= CA_ARG_PLAYER_1 &&
10024 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10025 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10028 /* ---------- execute action -------------------------------------------- */
10030 switch (action_type)
10037 /* ---------- level actions ------------------------------------------- */
10039 case CA_RESTART_LEVEL:
10041 game.restart_level = TRUE;
10046 case CA_SHOW_ENVELOPE:
10048 int element = getSpecialActionElement(action_arg_element,
10049 action_arg_number, EL_ENVELOPE_1);
10051 if (IS_ENVELOPE(element))
10052 local_player->show_envelope = element;
10057 case CA_SET_LEVEL_TIME:
10059 if (level.time > 0) /* only modify limited time value */
10061 TimeLeft = action_arg_number_new;
10064 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10066 DisplayGameControlValues();
10068 DrawGameValue_Time(TimeLeft);
10071 if (!TimeLeft && setup.time_limit)
10072 for (i = 0; i < MAX_PLAYERS; i++)
10073 KillPlayer(&stored_player[i]);
10079 case CA_SET_LEVEL_SCORE:
10081 local_player->score = action_arg_number_new;
10084 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10086 DisplayGameControlValues();
10088 DrawGameValue_Score(local_player->score);
10094 case CA_SET_LEVEL_GEMS:
10096 local_player->gems_still_needed = action_arg_number_new;
10099 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10101 DisplayGameControlValues();
10103 DrawGameValue_Emeralds(local_player->gems_still_needed);
10109 #if !USE_PLAYER_GRAVITY
10110 case CA_SET_LEVEL_GRAVITY:
10112 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10113 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10114 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10120 case CA_SET_LEVEL_WIND:
10122 game.wind_direction = action_arg_direction;
10127 /* ---------- player actions ------------------------------------------ */
10129 case CA_MOVE_PLAYER:
10131 /* automatically move to the next field in specified direction */
10132 for (i = 0; i < MAX_PLAYERS; i++)
10133 if (trigger_player_bits & (1 << i))
10134 stored_player[i].programmed_action = action_arg_direction;
10139 case CA_EXIT_PLAYER:
10141 for (i = 0; i < MAX_PLAYERS; i++)
10142 if (action_arg_player_bits & (1 << i))
10143 PlayerWins(&stored_player[i]);
10148 case CA_KILL_PLAYER:
10150 for (i = 0; i < MAX_PLAYERS; i++)
10151 if (action_arg_player_bits & (1 << i))
10152 KillPlayer(&stored_player[i]);
10157 case CA_SET_PLAYER_KEYS:
10159 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10160 int element = getSpecialActionElement(action_arg_element,
10161 action_arg_number, EL_KEY_1);
10163 if (IS_KEY(element))
10165 for (i = 0; i < MAX_PLAYERS; i++)
10167 if (trigger_player_bits & (1 << i))
10169 stored_player[i].key[KEY_NR(element)] = key_state;
10171 DrawGameDoorValues();
10179 case CA_SET_PLAYER_SPEED:
10181 for (i = 0; i < MAX_PLAYERS; i++)
10183 if (trigger_player_bits & (1 << i))
10185 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10187 if (action_arg == CA_ARG_SPEED_FASTER &&
10188 stored_player[i].cannot_move)
10190 action_arg_number = STEPSIZE_VERY_SLOW;
10192 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10193 action_arg == CA_ARG_SPEED_FASTER)
10195 action_arg_number = 2;
10196 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10199 else if (action_arg == CA_ARG_NUMBER_RESET)
10201 action_arg_number = level.initial_player_stepsize[i];
10205 getModifiedActionNumber(move_stepsize,
10208 action_arg_number_min,
10209 action_arg_number_max);
10211 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10218 case CA_SET_PLAYER_SHIELD:
10220 for (i = 0; i < MAX_PLAYERS; i++)
10222 if (trigger_player_bits & (1 << i))
10224 if (action_arg == CA_ARG_SHIELD_OFF)
10226 stored_player[i].shield_normal_time_left = 0;
10227 stored_player[i].shield_deadly_time_left = 0;
10229 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10231 stored_player[i].shield_normal_time_left = 999999;
10233 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10235 stored_player[i].shield_normal_time_left = 999999;
10236 stored_player[i].shield_deadly_time_left = 999999;
10244 #if USE_PLAYER_GRAVITY
10245 case CA_SET_PLAYER_GRAVITY:
10247 for (i = 0; i < MAX_PLAYERS; i++)
10249 if (trigger_player_bits & (1 << i))
10251 stored_player[i].gravity =
10252 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10253 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10254 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10255 stored_player[i].gravity);
10263 case CA_SET_PLAYER_ARTWORK:
10265 for (i = 0; i < MAX_PLAYERS; i++)
10267 if (trigger_player_bits & (1 << i))
10269 int artwork_element = action_arg_element;
10271 if (action_arg == CA_ARG_ELEMENT_RESET)
10273 (level.use_artwork_element[i] ? level.artwork_element[i] :
10274 stored_player[i].element_nr);
10276 #if USE_GFX_RESET_PLAYER_ARTWORK
10277 if (stored_player[i].artwork_element != artwork_element)
10278 stored_player[i].Frame = 0;
10281 stored_player[i].artwork_element = artwork_element;
10283 SetPlayerWaiting(&stored_player[i], FALSE);
10285 /* set number of special actions for bored and sleeping animation */
10286 stored_player[i].num_special_action_bored =
10287 get_num_special_action(artwork_element,
10288 ACTION_BORING_1, ACTION_BORING_LAST);
10289 stored_player[i].num_special_action_sleeping =
10290 get_num_special_action(artwork_element,
10291 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10298 /* ---------- CE actions ---------------------------------------------- */
10300 case CA_SET_CE_VALUE:
10302 #if USE_NEW_CUSTOM_VALUE
10303 int last_ce_value = CustomValue[x][y];
10305 CustomValue[x][y] = action_arg_number_new;
10307 if (CustomValue[x][y] != last_ce_value)
10309 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10310 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10312 if (CustomValue[x][y] == 0)
10314 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10315 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10323 case CA_SET_CE_SCORE:
10325 #if USE_NEW_CUSTOM_VALUE
10326 int last_ce_score = ei->collect_score;
10328 ei->collect_score = action_arg_number_new;
10330 if (ei->collect_score != last_ce_score)
10332 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10333 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10335 if (ei->collect_score == 0)
10339 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10340 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10343 This is a very special case that seems to be a mixture between
10344 CheckElementChange() and CheckTriggeredElementChange(): while
10345 the first one only affects single elements that are triggered
10346 directly, the second one affects multiple elements in the playfield
10347 that are triggered indirectly by another element. This is a third
10348 case: Changing the CE score always affects multiple identical CEs,
10349 so every affected CE must be checked, not only the single CE for
10350 which the CE score was changed in the first place (as every instance
10351 of that CE shares the same CE score, and therefore also can change)!
10353 SCAN_PLAYFIELD(xx, yy)
10355 if (Feld[xx][yy] == element)
10356 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10357 CE_SCORE_GETS_ZERO);
10366 /* ---------- engine actions ------------------------------------------ */
10368 case CA_SET_ENGINE_SCAN_MODE:
10370 InitPlayfieldScanMode(action_arg);
10380 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10382 int old_element = Feld[x][y];
10383 int new_element = GetElementFromGroupElement(element);
10384 int previous_move_direction = MovDir[x][y];
10385 #if USE_NEW_CUSTOM_VALUE
10386 int last_ce_value = CustomValue[x][y];
10388 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10389 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10390 boolean add_player_onto_element = (new_element_is_player &&
10391 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10392 /* this breaks SnakeBite when a snake is
10393 halfway through a door that closes */
10394 /* NOW FIXED AT LEVEL INIT IN files.c */
10395 new_element != EL_SOKOBAN_FIELD_PLAYER &&
10397 IS_WALKABLE(old_element));
10400 /* check if element under the player changes from accessible to unaccessible
10401 (needed for special case of dropping element which then changes) */
10402 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10403 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10411 if (!add_player_onto_element)
10413 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10414 RemoveMovingField(x, y);
10418 Feld[x][y] = new_element;
10420 #if !USE_GFX_RESET_GFX_ANIMATION
10421 ResetGfxAnimation(x, y);
10422 ResetRandomAnimationValue(x, y);
10425 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10426 MovDir[x][y] = previous_move_direction;
10428 #if USE_NEW_CUSTOM_VALUE
10429 if (element_info[new_element].use_last_ce_value)
10430 CustomValue[x][y] = last_ce_value;
10433 InitField_WithBug1(x, y, FALSE);
10435 new_element = Feld[x][y]; /* element may have changed */
10437 #if USE_GFX_RESET_GFX_ANIMATION
10438 ResetGfxAnimation(x, y);
10439 ResetRandomAnimationValue(x, y);
10442 DrawLevelField(x, y);
10444 if (GFX_CRUMBLED(new_element))
10445 DrawLevelFieldCrumbledSandNeighbours(x, y);
10449 /* check if element under the player changes from accessible to unaccessible
10450 (needed for special case of dropping element which then changes) */
10451 /* (must be checked after creating new element for walkable group elements) */
10452 #if USE_FIX_KILLED_BY_NON_WALKABLE
10453 if (IS_PLAYER(x, y) && !player_explosion_protected &&
10454 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10461 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10462 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10471 /* "ChangeCount" not set yet to allow "entered by player" change one time */
10472 if (new_element_is_player)
10473 RelocatePlayer(x, y, new_element);
10476 ChangeCount[x][y]++; /* count number of changes in the same frame */
10478 TestIfBadThingTouchesPlayer(x, y);
10479 TestIfPlayerTouchesCustomElement(x, y);
10480 TestIfElementTouchesCustomElement(x, y);
10483 static void CreateField(int x, int y, int element)
10485 CreateFieldExt(x, y, element, FALSE);
10488 static void CreateElementFromChange(int x, int y, int element)
10490 element = GET_VALID_RUNTIME_ELEMENT(element);
10492 #if USE_STOP_CHANGED_ELEMENTS
10493 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10495 int old_element = Feld[x][y];
10497 /* prevent changed element from moving in same engine frame
10498 unless both old and new element can either fall or move */
10499 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10500 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10505 CreateFieldExt(x, y, element, TRUE);
10508 static boolean ChangeElement(int x, int y, int element, int page)
10510 struct ElementInfo *ei = &element_info[element];
10511 struct ElementChangeInfo *change = &ei->change_page[page];
10512 int ce_value = CustomValue[x][y];
10513 int ce_score = ei->collect_score;
10514 int target_element;
10515 int old_element = Feld[x][y];
10517 /* always use default change event to prevent running into a loop */
10518 if (ChangeEvent[x][y] == -1)
10519 ChangeEvent[x][y] = CE_DELAY;
10521 if (ChangeEvent[x][y] == CE_DELAY)
10523 /* reset actual trigger element, trigger player and action element */
10524 change->actual_trigger_element = EL_EMPTY;
10525 change->actual_trigger_player = EL_PLAYER_1;
10526 change->actual_trigger_side = CH_SIDE_NONE;
10527 change->actual_trigger_ce_value = 0;
10528 change->actual_trigger_ce_score = 0;
10531 /* do not change elements more than a specified maximum number of changes */
10532 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10535 ChangeCount[x][y]++; /* count number of changes in the same frame */
10537 if (change->explode)
10544 if (change->use_target_content)
10546 boolean complete_replace = TRUE;
10547 boolean can_replace[3][3];
10550 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10553 boolean is_walkable;
10554 boolean is_diggable;
10555 boolean is_collectible;
10556 boolean is_removable;
10557 boolean is_destructible;
10558 int ex = x + xx - 1;
10559 int ey = y + yy - 1;
10560 int content_element = change->target_content.e[xx][yy];
10563 can_replace[xx][yy] = TRUE;
10565 if (ex == x && ey == y) /* do not check changing element itself */
10568 if (content_element == EL_EMPTY_SPACE)
10570 can_replace[xx][yy] = FALSE; /* do not replace border with space */
10575 if (!IN_LEV_FIELD(ex, ey))
10577 can_replace[xx][yy] = FALSE;
10578 complete_replace = FALSE;
10585 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10586 e = MovingOrBlocked2Element(ex, ey);
10588 is_empty = (IS_FREE(ex, ey) ||
10589 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10591 is_walkable = (is_empty || IS_WALKABLE(e));
10592 is_diggable = (is_empty || IS_DIGGABLE(e));
10593 is_collectible = (is_empty || IS_COLLECTIBLE(e));
10594 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10595 is_removable = (is_diggable || is_collectible);
10597 can_replace[xx][yy] =
10598 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
10599 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
10600 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
10601 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
10602 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
10603 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10604 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10606 if (!can_replace[xx][yy])
10607 complete_replace = FALSE;
10610 if (!change->only_if_complete || complete_replace)
10612 boolean something_has_changed = FALSE;
10614 if (change->only_if_complete && change->use_random_replace &&
10615 RND(100) < change->random_percentage)
10618 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10620 int ex = x + xx - 1;
10621 int ey = y + yy - 1;
10622 int content_element;
10624 if (can_replace[xx][yy] && (!change->use_random_replace ||
10625 RND(100) < change->random_percentage))
10627 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10628 RemoveMovingField(ex, ey);
10630 ChangeEvent[ex][ey] = ChangeEvent[x][y];
10632 content_element = change->target_content.e[xx][yy];
10633 target_element = GET_TARGET_ELEMENT(element, content_element, change,
10634 ce_value, ce_score);
10636 CreateElementFromChange(ex, ey, target_element);
10638 something_has_changed = TRUE;
10640 /* for symmetry reasons, freeze newly created border elements */
10641 if (ex != x || ey != y)
10642 Stop[ex][ey] = TRUE; /* no more moving in this frame */
10646 if (something_has_changed)
10648 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10649 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10655 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10656 ce_value, ce_score);
10658 if (element == EL_DIAGONAL_GROWING ||
10659 element == EL_DIAGONAL_SHRINKING)
10661 target_element = Store[x][y];
10663 Store[x][y] = EL_EMPTY;
10666 CreateElementFromChange(x, y, target_element);
10668 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10669 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10672 /* this uses direct change before indirect change */
10673 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10678 #if USE_NEW_DELAYED_ACTION
10680 static void HandleElementChange(int x, int y, int page)
10682 int element = MovingOrBlocked2Element(x, y);
10683 struct ElementInfo *ei = &element_info[element];
10684 struct ElementChangeInfo *change = &ei->change_page[page];
10687 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10688 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10691 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10692 x, y, element, element_info[element].token_name);
10693 printf("HandleElementChange(): This should never happen!\n");
10698 /* this can happen with classic bombs on walkable, changing elements */
10699 if (!CAN_CHANGE_OR_HAS_ACTION(element))
10702 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10703 ChangeDelay[x][y] = 0;
10709 if (ChangeDelay[x][y] == 0) /* initialize element change */
10711 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10713 if (change->can_change)
10716 /* !!! not clear why graphic animation should be reset at all here !!! */
10717 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10718 #if USE_GFX_RESET_WHEN_NOT_MOVING
10719 /* when a custom element is about to change (for example by change delay),
10720 do not reset graphic animation when the custom element is moving */
10721 if (!IS_MOVING(x, y))
10724 ResetGfxAnimation(x, y);
10725 ResetRandomAnimationValue(x, y);
10729 if (change->pre_change_function)
10730 change->pre_change_function(x, y);
10734 ChangeDelay[x][y]--;
10736 if (ChangeDelay[x][y] != 0) /* continue element change */
10738 if (change->can_change)
10740 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10742 if (IS_ANIMATED(graphic))
10743 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10745 if (change->change_function)
10746 change->change_function(x, y);
10749 else /* finish element change */
10751 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10753 page = ChangePage[x][y];
10754 ChangePage[x][y] = -1;
10756 change = &ei->change_page[page];
10759 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10761 ChangeDelay[x][y] = 1; /* try change after next move step */
10762 ChangePage[x][y] = page; /* remember page to use for change */
10767 if (change->can_change)
10769 if (ChangeElement(x, y, element, page))
10771 if (change->post_change_function)
10772 change->post_change_function(x, y);
10776 if (change->has_action)
10777 ExecuteCustomElementAction(x, y, element, page);
10783 static void HandleElementChange(int x, int y, int page)
10785 int element = MovingOrBlocked2Element(x, y);
10786 struct ElementInfo *ei = &element_info[element];
10787 struct ElementChangeInfo *change = &ei->change_page[page];
10790 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10793 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10794 x, y, element, element_info[element].token_name);
10795 printf("HandleElementChange(): This should never happen!\n");
10800 /* this can happen with classic bombs on walkable, changing elements */
10801 if (!CAN_CHANGE(element))
10804 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
10805 ChangeDelay[x][y] = 0;
10811 if (ChangeDelay[x][y] == 0) /* initialize element change */
10813 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10815 ResetGfxAnimation(x, y);
10816 ResetRandomAnimationValue(x, y);
10818 if (change->pre_change_function)
10819 change->pre_change_function(x, y);
10822 ChangeDelay[x][y]--;
10824 if (ChangeDelay[x][y] != 0) /* continue element change */
10826 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10828 if (IS_ANIMATED(graphic))
10829 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10831 if (change->change_function)
10832 change->change_function(x, y);
10834 else /* finish element change */
10836 if (ChangePage[x][y] != -1) /* remember page from delayed change */
10838 page = ChangePage[x][y];
10839 ChangePage[x][y] = -1;
10841 change = &ei->change_page[page];
10844 if (IS_MOVING(x, y)) /* never change a running system ;-) */
10846 ChangeDelay[x][y] = 1; /* try change after next move step */
10847 ChangePage[x][y] = page; /* remember page to use for change */
10852 if (ChangeElement(x, y, element, page))
10854 if (change->post_change_function)
10855 change->post_change_function(x, y);
10862 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10863 int trigger_element,
10865 int trigger_player,
10869 boolean change_done_any = FALSE;
10870 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10873 if (!(trigger_events[trigger_element][trigger_event]))
10877 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10878 trigger_event, recursion_loop_depth, recursion_loop_detected,
10879 recursion_loop_element, EL_NAME(recursion_loop_element));
10882 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10884 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10886 int element = EL_CUSTOM_START + i;
10887 boolean change_done = FALSE;
10890 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10891 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10894 for (p = 0; p < element_info[element].num_change_pages; p++)
10896 struct ElementChangeInfo *change = &element_info[element].change_page[p];
10898 if (change->can_change_or_has_action &&
10899 change->has_event[trigger_event] &&
10900 change->trigger_side & trigger_side &&
10901 change->trigger_player & trigger_player &&
10902 change->trigger_page & trigger_page_bits &&
10903 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10905 change->actual_trigger_element = trigger_element;
10906 change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10907 change->actual_trigger_side = trigger_side;
10908 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10909 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10911 if ((change->can_change && !change_done) || change->has_action)
10915 SCAN_PLAYFIELD(x, y)
10917 if (Feld[x][y] == element)
10919 if (change->can_change && !change_done)
10921 ChangeDelay[x][y] = 1;
10922 ChangeEvent[x][y] = trigger_event;
10924 HandleElementChange(x, y, p);
10926 #if USE_NEW_DELAYED_ACTION
10927 else if (change->has_action)
10929 ExecuteCustomElementAction(x, y, element, p);
10930 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10933 if (change->has_action)
10935 ExecuteCustomElementAction(x, y, element, p);
10936 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10942 if (change->can_change)
10944 change_done = TRUE;
10945 change_done_any = TRUE;
10952 RECURSION_LOOP_DETECTION_END();
10954 return change_done_any;
10957 static boolean CheckElementChangeExt(int x, int y,
10959 int trigger_element,
10961 int trigger_player,
10964 boolean change_done = FALSE;
10967 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10968 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10971 if (Feld[x][y] == EL_BLOCKED)
10973 Blocked2Moving(x, y, &x, &y);
10974 element = Feld[x][y];
10978 /* check if element has already changed */
10979 if (Feld[x][y] != element)
10982 /* check if element has already changed or is about to change after moving */
10983 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10984 Feld[x][y] != element) ||
10986 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10987 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10988 ChangePage[x][y] != -1)))
10993 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10994 trigger_event, recursion_loop_depth, recursion_loop_detected,
10995 recursion_loop_element, EL_NAME(recursion_loop_element));
10998 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11000 for (p = 0; p < element_info[element].num_change_pages; p++)
11002 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11004 /* check trigger element for all events where the element that is checked
11005 for changing interacts with a directly adjacent element -- this is
11006 different to element changes that affect other elements to change on the
11007 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11008 boolean check_trigger_element =
11009 (trigger_event == CE_TOUCHING_X ||
11010 trigger_event == CE_HITTING_X ||
11011 trigger_event == CE_HIT_BY_X ||
11013 /* this one was forgotten until 3.2.3 */
11014 trigger_event == CE_DIGGING_X);
11017 if (change->can_change_or_has_action &&
11018 change->has_event[trigger_event] &&
11019 change->trigger_side & trigger_side &&
11020 change->trigger_player & trigger_player &&
11021 (!check_trigger_element ||
11022 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11024 change->actual_trigger_element = trigger_element;
11025 change->actual_trigger_player = EL_PLAYER_1 + log_2(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);